566 lines
26 KiB
HTML
566 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, user-scalable=no" />
|
|
<link rel="shortcut icon" type="image/png" href="favicon.png">
|
|
<title>Inscribe the planet</title>
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
font-size: 1.15rem;
|
|
font-family: Arial, sans-serif;
|
|
background-color: black;
|
|
color: white;
|
|
font-family: monospace;
|
|
}
|
|
html {
|
|
max-width: 70ch;
|
|
padding: 3rem 1rem;
|
|
margin: auto;
|
|
line-height: 1.25;
|
|
}
|
|
h1 {
|
|
font-size: 2rem;
|
|
}
|
|
h2 {
|
|
font-size: 1.5rem;
|
|
}
|
|
input {
|
|
line-height: 1.25;
|
|
width: 100%;
|
|
height: 1.8rem;
|
|
font-size: 1.15rem;
|
|
border: 1px solid white;
|
|
}
|
|
.black-bg{
|
|
display:none;
|
|
width:100%;
|
|
position:fixed;
|
|
top:0;
|
|
left:0;
|
|
background-color:black;
|
|
opacity:.5;
|
|
width:100vw;
|
|
height:100vh;
|
|
}
|
|
.modal{
|
|
display:none;
|
|
position:fixed;
|
|
box-sizing:border-box;
|
|
top:50%;
|
|
left:50%;
|
|
transform:translate(-50%,-50%);
|
|
width:100%;
|
|
max-width:560px;
|
|
background-color:white;
|
|
border-radius:10px;
|
|
padding:20px;
|
|
color:black;
|
|
text-align:center;
|
|
}
|
|
.display {
|
|
display: none;
|
|
border: 1px solid white;
|
|
padding: 1rem;
|
|
border-radius: 1rem;
|
|
word-wrap: break-word;
|
|
text-align: center;
|
|
position: relative;
|
|
}
|
|
.display p {
|
|
text-align: left;
|
|
}
|
|
.checking_mempool, .checking_mempool span {
|
|
color: lightgreen;
|
|
}
|
|
.feerates {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
}
|
|
.fee {
|
|
width: 100%;
|
|
max-width: 6rem;
|
|
margin-right: 2rem;
|
|
margin-left: 0px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
}
|
|
.fee .num {
|
|
background-color: grey;
|
|
border: 1px solid white;
|
|
padding: .3rem;
|
|
}
|
|
.maxfee .num {
|
|
background-color: green;
|
|
}
|
|
@media screen and (max-width: 600px) {
|
|
}
|
|
</style>
|
|
<script src="https://unpkg.com/@cmdcode/bton@1.0.3"></script>
|
|
<script> const BTON = window.bton </script>
|
|
<script src="https://unpkg.com/@cmdcode/crypto-utils@1.5.11"></script>
|
|
<script src="https://bundle.run/bech32@2.0.0"></script>
|
|
<script src="https://supertestnet.github.io/bitcoin-chess/js/qrcode.js"></script>
|
|
<script>
|
|
var $ = document.querySelector.bind( document );
|
|
var $$ = document.querySelectorAll.bind( document );
|
|
var url_params = new URLSearchParams( window.location.search );
|
|
var url_keys = url_params.keys();
|
|
var $_GET = {}
|
|
for ( var key of url_keys ) $_GET[ key ] = url_params.get( key );
|
|
</script>
|
|
<script>
|
|
function encodeBase64( file ) {
|
|
return new Promise( function( resolve, reject ) {
|
|
//the next three lines handle the case where an image is passed in from an external site
|
|
if ( "files" in file ) {
|
|
file = file.files[ 0 ];
|
|
}
|
|
sessionStorage[ "mimetype" ] = file.type;
|
|
var imgReader = new FileReader();
|
|
imgReader.onloadend = function() {
|
|
resolve( imgReader.result.toString() );
|
|
}
|
|
imgReader.readAsDataURL( file );
|
|
});
|
|
}
|
|
function base64ToHex(str) {
|
|
const raw = atob(str);
|
|
let result = '';
|
|
for (let i = 0; i < raw.length; i++) {
|
|
const hex = raw.charCodeAt(i).toString(16);
|
|
result += (hex.length === 2 ? hex : '0' + hex);
|
|
}
|
|
return result.toLowerCase();
|
|
}
|
|
function hexToBytes( hex ) {
|
|
return Uint8Array.from( hex.match( /.{1,2}/g ).map( ( byte ) => parseInt( byte, 16 ) ) );
|
|
}
|
|
function bytesToHex( bytes ) {
|
|
return bytes.reduce( ( str, byte ) => str + byte.toString( 16 ).padStart( 2, "0" ), "" );
|
|
}
|
|
function createQR( content ) {
|
|
var dataUriPngImage = document.createElement( "img" ),
|
|
s = QRCode.generatePNG( content, {
|
|
ecclevel: "M",
|
|
format: "html",
|
|
fillcolor: "#FFFFFF",
|
|
textcolor: "#000000",
|
|
margin: 4,
|
|
modulesize: 8,
|
|
});
|
|
dataUriPngImage.src = s;
|
|
dataUriPngImage.id = "qr_code";
|
|
return dataUriPngImage;
|
|
}
|
|
async function getBitcoinPriceFromCoinbase() {
|
|
var data = await getData( "https://api.coinbase.com/v2/prices/BTC-USD/spot" );
|
|
var json = JSON.parse( data );
|
|
var price = json[ "data" ][ "amount" ];
|
|
return price;
|
|
}
|
|
|
|
async function getBitcoinPriceFromKraken() {
|
|
var data = await getData( "https://api.kraken.com/0/public/Ticker?pair=XBTUSD" );
|
|
var json = JSON.parse( data );
|
|
var price = json[ "result" ][ "XXBTZUSD" ][ "a" ][ 0 ];
|
|
return price;
|
|
}
|
|
|
|
async function getBitcoinPriceFromCoindesk() {
|
|
var data = await getData( "https://api.coindesk.com/v1/bpi/currentprice.json" );
|
|
var json = JSON.parse( data );
|
|
var price = json[ "bpi" ][ "USD" ][ "rate_float" ];
|
|
return price;
|
|
}
|
|
|
|
async function getBitcoinPriceFromGemini() {
|
|
var data = await getData( "https://api.gemini.com/v2/ticker/BTCUSD" );
|
|
var json = JSON.parse( data );
|
|
var price = json[ "bid" ];
|
|
return price;
|
|
}
|
|
|
|
async function getBitcoinPriceFromBybit() {
|
|
var data = await getData( "https://api-testnet.bybit.com/derivatives/v3/public/order-book/L2?category=linear&symbol=BTCUSDT" );
|
|
var json = JSON.parse( data );
|
|
var price = json[ "result" ][ "b" ][ 0 ][ 0 ];
|
|
return price;
|
|
}
|
|
|
|
async function getBitcoinPrice() {
|
|
var prices = [];
|
|
var cbprice = await getBitcoinPriceFromCoinbase();
|
|
var kprice = await getBitcoinPriceFromKraken();
|
|
var cdprice = await getBitcoinPriceFromCoindesk();
|
|
var gprice = await getBitcoinPriceFromGemini();
|
|
var bprice = await getBitcoinPriceFromBybit();
|
|
prices.push( Number( cbprice ), Number( kprice ), Number( cdprice ), Number( gprice ), Number( bprice ) );
|
|
prices.sort();
|
|
return prices[ 2 ];
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<h1>Welcome to the online inscriber</h1>
|
|
<p>First enter a <span class="type_of_address">taproot</span> address from an ordinal wallet</p>
|
|
<p><input class="address"></p>
|
|
<p>Then upload a file you want that address to own</p>
|
|
<form>
|
|
<p><input type="file" class="form" /></p>
|
|
<p>Select a feerate (measured in sats per byte)</p>
|
|
<div class="feerates"><div class="fee minfee"><div class="num">...</div><div class="name">Min</div></div><div class="fee midfee"><div class="num">...</div><div class="name">Mid</div></div><div class="fee maxfee"><div class="num">...</div><div class="name">Max</div></div></div>
|
|
<div id="sliderange">
|
|
<p>Sats per byte: <span id="sats_per_byte">1</span></p>
|
|
<input id="sats_range" type="range" min="1" max="100" value="1">
|
|
</div>
|
|
<script>
|
|
var slider = document.getElementById( "sats_range" );
|
|
var output = document.getElementById( "sats_per_byte" );
|
|
output.innerHTML = slider.value;
|
|
slider.oninput = function() {
|
|
output.innerHTML = this.value;
|
|
sessionStorage[ "feerate" ] = this.value;
|
|
$$( '.fee .num' ).forEach( function( item ) {
|
|
item.style.backgroundColor = "grey";
|
|
});
|
|
}
|
|
</script>
|
|
</form>
|
|
<p><button class="submit">Submit</button></p>
|
|
<div class="display"></div>
|
|
<div style="display: none;" class="file"></div>
|
|
<script>
|
|
sessionStorage.clear();
|
|
const privkey = bytesToHex( cryptoUtils.Noble.utils.randomPrivateKey() );
|
|
$( '.form' ).addEventListener( "change", async function() {
|
|
if ( this.files[ 0 ].size >= 120000 && this.files[ 0 ].size < 350000 ) {
|
|
var conf = confirm( "You exceeded the 120 kb size limit. Click ok to override this warning or click cancel to try again." );
|
|
if ( conf ) {
|
|
var b64 = await encodeBase64( this );
|
|
} else {
|
|
this.value = null;
|
|
}
|
|
} else if ( this.files[ 0 ].size >= 350000 ) {
|
|
alert( "You exceeded this software's hard size limit of 350 kb files. Modify the code to override the limit." )
|
|
this.value = null;
|
|
}
|
|
var base64 = b64.substring( b64.indexOf( "base64," ) + 7 );
|
|
var hex = base64ToHex( base64 );
|
|
// console.log( "hex:", hex );
|
|
// console.log( "bytes:", hexToBytes( hex ) );
|
|
$( '.file' ).innerText = hex;
|
|
});
|
|
$( '.submit' ).addEventListener( "click", async function() {
|
|
const ec = new TextEncoder();
|
|
const hex = $( '.file' ).innerText;
|
|
const data = hexToBytes( hex );
|
|
const KeyPair = cryptoUtils.KeyPair;
|
|
const seckey = new KeyPair(privkey);
|
|
const pubkey = seckey.pub.rawX;
|
|
const mimetype = ec.encode( sessionStorage[ "mimetype" ] );
|
|
const script = [ pubkey, 'OP_CHECKSIG', 'OP_0', 'OP_IF', ec.encode('ord'), '01', mimetype, 'OP_0', data, 'OP_ENDIF' ];
|
|
const leaf = await BTON.Tap.getLeaf(BTON.Script.encode(script));
|
|
const [ tapkey ] = await BTON.Tap.getPubkey(pubkey, [ leaf ]);
|
|
const cblock = await BTON.Tap.getPath(pubkey, leaf);
|
|
console.log('Tapkey:', tapkey);
|
|
var fundingAddress = BTON.Tap.encodeAddress( tapkey, 'bc' );
|
|
console.log('Funding address: ', fundingAddress );
|
|
//var toAddress = prompt( "give me the address you your inscription to arrive in and consider using this one: tb1padwymyc6x2dfc3cwzvpxxcqsr3c46cjx9zxg0jmcuf44fjuwljxs7x68fm" );
|
|
//var toAddress = "tb1padwymyc6x2dfc3cwzvpxxcqsr3c46cjx9zxg0jmcuf44fjuwljxs7x68fm";
|
|
var toAddress = $( '.address' ).value;
|
|
console.log('Address that will receive the inscription:', toAddress );
|
|
var decodedToAddress = "5120" + BTON.Tap.decodeAddress( toAddress ).hex;
|
|
console.log('To address decoded:', decodedToAddress );
|
|
var txsize = 200 + ( hex.length / 2 );
|
|
if ( sessionStorage[ "feerate" ] ) {
|
|
var feerate = Number( sessionStorage[ "feerate" ] );
|
|
} else {
|
|
var feerate = await getMaxFeeRate();
|
|
}
|
|
var fee = feerate * txsize;
|
|
var btc_price = sessionStorage[ "bitcoin_price" ];
|
|
var sats_price = await satsToDollars( fee + 1000 );
|
|
sats_price = Math.floor( sats_price * 100 ) / 100;
|
|
var html = `<p>Please send at least ${fee + 1000} sats ($${sats_price}) to this address:</p><p>${fundingAddress}</p>`;
|
|
$( '.display' ).innerHTML = html;
|
|
var qr_value = "bitcoin:" + fundingAddress + "?amount=" + satsToBitcoin( fee + 1000 );
|
|
console.log( "qr:", qr_value );
|
|
$( '.display' ).append( createQR( qr_value ) );
|
|
$( '.display' ).innerHTML += `<p class="checking_mempool">Checking the mempool<span class="dots">.</span></p>`;
|
|
$( '.display' ).innerHTML += `<p>1000 sats will go to the address</p><p>${fee} sats will go to miners as a mining fee</p>`;
|
|
$( '.display' ).style.display = "block";
|
|
await loopTilAddressReceivesMoney( fundingAddress );
|
|
await waitSomeSeconds( 2 );
|
|
var txinfo = await addressReceivedMoneyInThisTx( fundingAddress );
|
|
var txid = txinfo[ 0 ];
|
|
var vout = txinfo[ 1 ];
|
|
var amt = txinfo[ 2 ];
|
|
// if ( !txid || !vout || !amt ) {
|
|
// await waitSomeSeconds( 2 );
|
|
// txinfo = await addressReceivedMoneyInThisTx( fundingAddress );
|
|
// txid = txinfo[ 0 ];
|
|
// vout = txinfo[ 1 ];
|
|
// amt = txinfo[ 2 ];
|
|
// }
|
|
// var amt = prompt( "give me the amount you sent" );
|
|
// amt = Number( amt );
|
|
// var txid = prompt( "give me the txid" );
|
|
// var vout = prompt( "give me the vout" );
|
|
// vout = Number( vout );
|
|
console.log( "yay! txid:", txid, "vout:", vout, "amount:", amt );
|
|
console.log( "feerate:", feerate, "txsize:", txsize, "fee:", fee );
|
|
console.log( "fee:", fee );
|
|
const redeemtx = {
|
|
version: 2,
|
|
input: [{
|
|
txid: txid,
|
|
vout: vout,
|
|
prevout: { value: amt, scriptPubKey: '5120' + tapkey },
|
|
witness: []
|
|
}],
|
|
output:[{
|
|
value: amt - fee,
|
|
scriptPubKey: decodedToAddress
|
|
}],
|
|
locktime: 0
|
|
}
|
|
const sec = await BTON.Tap.getSeckey(seckey.raw, [ leaf ]);
|
|
const sig = await BTON.Sig.taproot.sign(seckey.raw, redeemtx, 0, { extention: leaf });
|
|
redeemtx.input[0].witness = [ sig, script, cblock ];
|
|
console.dir(redeemtx, { depth: null });
|
|
//await BTON.Sig.taproot.verify(redeemtx, 0, {extension: leaf}, true);
|
|
var rawtx = BTON.Tx.encode(redeemtx);
|
|
console.log('Txdata:', rawtx );
|
|
var txid = await pushBTCpmt( rawtx );
|
|
var html = `<p style="background-color: white; color: black;">Success! Your image will shortly be inscribed on the bitcoin blockchain in this transaction:</p><p style="word-wrap: break-word;"><a href="https://mempool.space/tx/${txid}" target="_blank">https://mempool.space/tx/${txid}</a></p>`;
|
|
html += `<p style="background-color: white; color: black;">When your transaction confirms you can view it on the ordinals explorer here:</p><p style="word-wrap: break-word;"><a href="https://ordinals.com/inscription/${txid}i0" target="_blank">https://ordinals.com/inscription/${txid}i0</a></p>`;
|
|
$( '.modal' ).innerHTML = html;
|
|
$( '.modal' ).style.display = "block";
|
|
$( '.black-bg' ).style.display = "block";
|
|
});
|
|
async function getMaxFeeRate() {
|
|
var fees = await getData( "https://mempool.space/api/v1/fees/recommended" );
|
|
fees = JSON.parse( fees );
|
|
// if ( !( "minimumFee" in fees ) ) return "error -- site down";
|
|
// var minfee = fees[ "minimumFee" ];
|
|
if ( !( "fastestFee" in fees ) ) return "error -- site down";
|
|
var minfee = fees[ "fastestFee" ];
|
|
return minfee;
|
|
}
|
|
function isValidTaprootAddress( address ) {
|
|
try {
|
|
BTON.Tap.decodeAddress( address ).hex;
|
|
return true;
|
|
} catch( e ) {}
|
|
return;
|
|
}
|
|
async function getAllFeeRates() {
|
|
var fees = await getData( "https://mempool.space/api/v1/fees/recommended" );
|
|
fees = JSON.parse( fees );
|
|
return fees;
|
|
}
|
|
function getData( url ) {
|
|
return new Promise( async function( resolve, reject ) {
|
|
function inner_get( url ) {
|
|
var xhttp = new XMLHttpRequest();
|
|
xhttp.open( "GET", url, true );
|
|
xhttp.send();
|
|
return xhttp;
|
|
}
|
|
var data = inner_get( url );
|
|
data.onerror = function( e ) {
|
|
resolve( "error" );
|
|
}
|
|
async function isResponseReady() {
|
|
return new Promise( function( resolve2, reject ) {
|
|
if ( !data.responseText || data.readyState != 4 ) {
|
|
setTimeout( async function() {
|
|
var msg = await isResponseReady();
|
|
resolve2( msg );
|
|
}, 1 );
|
|
} else {
|
|
resolve2( data.responseText );
|
|
}
|
|
});
|
|
}
|
|
var returnable = await isResponseReady();
|
|
resolve( returnable );
|
|
});
|
|
}
|
|
async function pushBTCpmt( rawtx ) {
|
|
var txid = await postData( "https://mempool.space/api/tx", rawtx );
|
|
return txid;
|
|
}
|
|
function waitSomeSeconds( num ) {
|
|
var num = num.toString() + "000";
|
|
num = Number( num );
|
|
return new Promise( function( resolve, reject ) {
|
|
setTimeout( function() { resolve( "" ); }, num );
|
|
});
|
|
}
|
|
async function postData( url, json, content_type = "", apikey = "" ) {
|
|
var rtext = "";
|
|
function inner_post( url, json, content_type = "", apikey = "" ) {
|
|
var xhttp = new XMLHttpRequest();
|
|
xhttp.open( "POST", url, true );
|
|
if ( content_type ) {
|
|
xhttp.setRequestHeader( `Content-Type`, content_type );
|
|
}
|
|
if ( apikey ) {
|
|
xhttp.setRequestHeader( `X-Api-Key`, apikey );
|
|
}
|
|
xhttp.send( json );
|
|
return xhttp;
|
|
}
|
|
var data = inner_post( url, json, content_type, apikey );
|
|
data.onerror = function( e ) {
|
|
rtext = "error";
|
|
}
|
|
async function isResponseReady() {
|
|
return new Promise( function( resolve, reject ) {
|
|
if ( rtext == "error" ) {
|
|
resolve( rtext );
|
|
}
|
|
if ( !data.responseText || data.readyState != 4 ) {
|
|
setTimeout( async function() {
|
|
var msg = await isResponseReady();
|
|
resolve( msg );
|
|
}, 50 );
|
|
} else {
|
|
resolve( data.responseText );
|
|
}
|
|
});
|
|
}
|
|
var returnable = await isResponseReady();
|
|
return returnable;
|
|
}
|
|
async function loopTilAddressReceivesMoney( address ) {
|
|
var itReceivedMoney = false;
|
|
async function isDataSetYet( data_i_seek ) {
|
|
return new Promise( function( resolve, reject ) {
|
|
if ( !data_i_seek ) {
|
|
setTimeout( async function() {
|
|
console.log( "waiting for address to receive money..." );
|
|
itReceivedMoney = await addressOnceHadMoney( address );
|
|
var msg = await isDataSetYet( itReceivedMoney );
|
|
resolve( msg );
|
|
}, 2000 );
|
|
} else {
|
|
resolve( data_i_seek );
|
|
}
|
|
});
|
|
}
|
|
async function getTimeoutData() {
|
|
var data_i_seek = await isDataSetYet( itReceivedMoney );
|
|
return data_i_seek;
|
|
}
|
|
var returnable = await getTimeoutData();
|
|
return returnable;
|
|
}
|
|
|
|
async function addressReceivedMoneyInThisTx( address ) {
|
|
var txid;
|
|
var vout;
|
|
var amt;
|
|
var nonjson = await getData( "https://mempool.space/api/address/" + address + "/txs" );
|
|
var json = JSON.parse( nonjson );
|
|
json.forEach( function( tx ) {
|
|
tx[ "vout" ].forEach( function( output, index ) {
|
|
if ( output[ "scriptpubkey_address" ] == address ) {
|
|
txid = tx[ "txid" ];
|
|
vout = index;
|
|
amt = output[ "value" ];
|
|
}
|
|
});
|
|
});
|
|
return [txid, vout, amt];
|
|
}
|
|
async function addressOnceHadMoney( address ) {
|
|
var url = "https://mempool.space/api/address/" + address;
|
|
var nonjson = await getData( url );
|
|
var json = JSON.parse( nonjson );
|
|
if ( json[ "chain_stats" ][ "tx_count" ] > 0 || json[ "mempool_stats" ][ "tx_count" ] > 0 ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function dotLoop( string ) {
|
|
if ( !$( '.dots' ) ) {
|
|
setTimeout( function() {dotLoop( string );}, 1000 );
|
|
return;
|
|
}
|
|
if ( string.length < 3 ) {
|
|
string = string + ".";
|
|
} else {
|
|
string = ".";
|
|
}
|
|
$( '.dots' ).innerText = string;
|
|
setTimeout( function() {dotLoop( string );}, 1000 );
|
|
}
|
|
dotLoop( "." );
|
|
function satsToBitcoin( sats ) {
|
|
if ( sats >= 100000000 ) sats = sats * 10;
|
|
var string = String( sats ).padStart( 8, "0" ).slice( 0,-9 ) + "." + String( sats ).padStart( 8, "0" ).slice( -9 );
|
|
if ( string.substring( 0, 1 ) == "." ) string = "0" + string;
|
|
return string;
|
|
}
|
|
|
|
async function satsToDollars( sats ) {
|
|
if ( sats >= 100000000 ) sats = sats * 10;
|
|
var bitcoin_price = sessionStorage[ "bitcoin_price" ];
|
|
var value_in_dollars = Number( String( sats ).padStart( 8, "0" ).slice( 0,-9 ) + "." + String( sats ).padStart( 8, "0" ).slice( -9 ) ) * bitcoin_price;
|
|
return value_in_dollars;
|
|
}
|
|
function modalVanish() {
|
|
$( ".black-bg" ).style.display = "none";
|
|
$( ".modal" ).style.display = "none";
|
|
}
|
|
$$( '.fee' ).forEach( function( item ) {
|
|
item.onclick = function() {
|
|
$$( '.fee .num' ).forEach( function( item2 ) {
|
|
item2.style.backgroundColor = "grey";
|
|
});
|
|
this.getElementsByClassName( "num" )[ 0 ].style.backgroundColor = "green";
|
|
sessionStorage[ "feerate" ] = this.getElementsByClassName( "num" )[ 0 ].innerText;
|
|
$( '#sats_per_byte' ).innerText = Number( this.getElementsByClassName( "num" )[ 0 ].innerText );
|
|
$( '#sats_range' ).value = Number( this.getElementsByClassName( "num" )[ 0 ].innerText );
|
|
}
|
|
});
|
|
function checkAddress() {
|
|
if ( !isValidTaprootAddress( $( '.address' ).value ) ) {
|
|
$( '.address' ).style.backgroundColor = "#ff5252";
|
|
$( '.address' ).style.border = "2px solid red";
|
|
$( '.type_of_address' ).style.border = "1px solid white";
|
|
} else {
|
|
$( '.address' ).style.backgroundColor = "initial";
|
|
$( '.address' ).style.border = "1px solid white";
|
|
$( '.type_of_address' ).style.borderStyle = "none";
|
|
}
|
|
}
|
|
$( '.address' ).onchange = checkAddress;
|
|
$( '.address' ).onpaste = checkAddress;
|
|
$( '.address' ).onkeyup = checkAddress;
|
|
async function init( num ) {
|
|
if ( !num ) {
|
|
var maxfee = await getMaxFeeRate();
|
|
$( '#sats_per_byte' ).innerText = maxfee;
|
|
$( '#sats_range' ).value = maxfee;
|
|
}
|
|
num = num + 1;
|
|
var allrates = await getAllFeeRates();
|
|
$( '.minfee .num' ).innerText = allrates[ "minimumFee" ];
|
|
$( '.midfee .num' ).innerText = allrates[ "hourFee" ];
|
|
$( '.maxfee .num' ).innerText = allrates[ "fastestFee" ];
|
|
sessionStorage[ "bitcoin_price" ] = await getBitcoinPrice();
|
|
await waitSomeSeconds( 10 );
|
|
init( num );
|
|
}
|
|
init( 0 );
|
|
</script>
|
|
<div class="black-bg" onclick="modalVanish();"></div>
|
|
<div class="modal"></div>
|
|
</body>
|
|
</html>
|