2023-03-14 10:27:09 -05:00
<!DOCTYPE html>
< html >
2023-03-29 02:37:31 +02:00
< head >
< meta charset = "UTF-8" >
2023-04-01 19:41:32 +02:00
< meta name = "viewport" content = "width=device-width, user-scalable=no" />
2023-03-29 02:37:31 +02:00
< link rel = "shortcut icon" type = "image/png" href = "favicon.png" >
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< title > Inscribe the planet</ title >
< style >
2023-04-01 19:41:32 +02:00
* {
box-sizing : border-box ;
font-size : 1.15 rem ;
font-family : monospace ;
background-color : black ;
color : white ;
}
html {
max-width : 70 ch ;
padding : 3 rem 1 rem ;
margin : auto ;
line-height : 1.25 ;
}
ul li a {
text-decoration : none !important ;
}
a . active : before
{
content : '['
}
a . active : after
{
content : ']'
}
h1 {
font-size : 2 rem ;
}
h2 {
font-size : 1.5 rem ;
}
label
{
display : block ;
margin-bottom : 10 px ;
}
input {
line-height : 1.25 ;
width : 100 % ;
height : 1.8 rem ;
font-size : 1.15 rem ;
border : 1 px solid white ;
margin-bottom : 10 px ;
}
. black-bg {
display : none ;
2023-03-29 02:37:31 +02:00
width : 100 % ;
2023-04-01 19:41:32 +02:00
position : fixed ;
top : 0 ;
left : 0 ;
background-color : black ;
opacity : .5 ;
width : 100 vw ;
height : 100 vh ;
}
. modal {
display : none ;
position : fixed ;
box-sizing : border-box ;
top : 50 % ;
left : 50 % ;
transform : translate ( -50 % , -50 % );
width : 100 % ;
max-width : 560 px ;
2023-03-29 02:37:31 +02:00
max-height : 100 vh ;
2023-04-01 19:41:32 +02:00
background-color : white ;
2023-03-29 02:37:31 +02:00
padding : 20 px ;
2023-04-01 19:41:32 +02:00
color : black ;
text-align : center ;
2023-03-29 02:37:31 +02:00
border-radius : 25 px ;
border : 2 px solid var ( -- primary - color );
}
2023-04-01 19:41:32 +02:00
. modal-content {
2023-03-29 02:37:31 +02:00
overflow-y : auto ;
max-height : 85 vh ;
2023-04-01 19:41:32 +02:00
background-color : white ;
color : black ;
text-align : center ;
2023-03-29 02:37:31 +02:00
border : transparent ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. display {
display : none ;
border : 1 px solid white ;
padding : 1 rem ;
border-radius : 1 rem ;
word-wrap : break-word ;
text-align : center ;
position : relative ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. display p {
text-align : left ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. checking_mempool , . checking_mempool span {
color : lightgreen ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. feerates {
display : flex ;
justify-content : space-around ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. fee {
width : 100 % ;
max-width : 6 rem ;
margin : 0 px 0.5 rem ;
text-align : center ;
cursor : pointer ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. fee . num {
background-color : grey ;
border : 1 px solid white ;
padding : .3 rem ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. minfee . num {
background-color : green ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. safari_warning {
display : none ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. registration , . upload_file {
2023-04-01 19:41:32 +02:00
text-decoration : underline ;
2023-03-29 02:37:31 +02:00
cursor : pointer ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
. dns_form , . dns_checker , . timer , . brc20_deploy_form , . brc20_mint_form {
display : none ;
}
</ style >
< script src = "https://bundle.run/buffer@6.0.3" ></ script >
< script > const buf = buffer </ script >
< script src = "bton.min.js" ></ 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 >
2023-04-01 19:41:32 +02:00
let $ = document . querySelector . bind ( document );
let $$ = document . querySelectorAll . bind ( document );
let url_params = new URLSearchParams ( window . location . search );
2023-03-29 02:37:31 +02:00
let url_keys = url_params . keys ();
let $_GET = {}
2023-04-01 19:41:32 +02:00
for ( let key of url_keys ) $_GET [ key ] = url_params . get ( key );
2023-03-29 02:37:31 +02:00
</ script >
< script >
2023-04-01 19:41:32 +02:00
function encodeBase64 ( file ) {
return new Promise ( function ( resolve , reject ) {
2023-03-29 02:37:31 +02:00
let imgReader = new FileReader ();
2023-04-01 19:41:32 +02:00
imgReader . onloadend = function () {
resolve ( imgReader . result . toString ());
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
imgReader . readAsDataURL ( file );
2023-03-29 02:37:31 +02:00
});
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
function base64ToHex ( str ) {
const raw = atob ( str );
let result = '' ;
for ( let i = 0 ; i < raw . length ; i ++ ) {
2023-03-14 10:27:09 -05:00
const hex = raw . charCodeAt ( i ). toString ( 16 );
result += ( hex . length === 2 ? hex : '0' + hex );
}
2023-03-29 02:37:31 +02:00
return result . toLowerCase ();
}
2023-04-01 19:41:32 +02:00
function hexToBytes ( hex ) {
return Uint8Array . from ( hex . match ( /.{1,2}/g ). map (( byte ) => parseInt ( byte , 16 )));
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
function bytesToHex ( bytes ) {
return bytes . reduce (( str , byte ) => str + byte . toString ( 16 ). padStart ( 2 , "0" ), "" );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
function textToHex ( text ) {
var encoder = new TextEncoder (). encode ( text );
2023-03-29 02:37:31 +02:00
return [... new Uint8Array ( encoder )]
2023-04-01 19:41:32 +02:00
. map ( x => x . toString ( 16 ). padStart ( 2 , "0" ))
. join ( "" );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
function createQR ( content ) {
let dataUriPngImage = document . createElement ( "img" ),
s = QRCode . generatePNG ( content , {
2023-03-14 10:27:09 -05:00
ecclevel : "M" ,
format : "html" ,
fillcolor : "#FFFFFF" ,
textcolor : "#000000" ,
margin : 4 ,
modulesize : 8 ,
});
2023-03-29 02:37:31 +02:00
dataUriPngImage . src = s ;
dataUriPngImage . id = "qr_code" ;
return dataUriPngImage ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
async function getBitcoinPriceFromCoinbase () {
2023-04-01 19:41:32 +02:00
let data = await getData ( "https://api.coinbase.com/v2/prices/BTC-USD/spot" );
let json = JSON . parse ( data );
let price = json [ "data" ][ "amount" ];
2023-03-29 02:37:31 +02:00
return price ;
}
2023-03-14 10:27:09 -05:00
2023-03-29 02:37:31 +02:00
async function getBitcoinPriceFromKraken () {
2023-04-01 19:41:32 +02:00
let data = await getData ( "https://api.kraken.com/0/public/Ticker?pair=XBTUSD" );
let json = JSON . parse ( data );
let price = json [ "result" ][ "XXBTZUSD" ][ "a" ][ 0 ];
2023-03-29 02:37:31 +02:00
return price ;
}
2023-03-14 10:27:09 -05:00
2023-03-29 02:37:31 +02:00
async function getBitcoinPriceFromCoindesk () {
2023-04-01 19:41:32 +02:00
let data = await getData ( "https://api.coindesk.com/v1/bpi/currentprice.json" );
let json = JSON . parse ( data );
let price = json [ "bpi" ][ "USD" ][ "rate_float" ];
2023-03-29 02:37:31 +02:00
return price ;
}
2023-03-14 10:27:09 -05:00
2023-03-29 02:37:31 +02:00
async function getBitcoinPriceFromGemini () {
2023-04-01 19:41:32 +02:00
let data = await getData ( "https://api.gemini.com/v2/ticker/BTCUSD" );
let json = JSON . parse ( data );
let price = json [ "bid" ];
2023-03-29 02:37:31 +02:00
return price ;
}
2023-03-14 10:27:09 -05:00
2023-03-29 02:37:31 +02:00
async function getBitcoinPriceFromBybit () {
2023-04-01 19:41:32 +02:00
let data = await getData ( "https://api-testnet.bybit.com/derivatives/v3/public/order-book/L2?category=linear&symbol=BTCUSDT" );
let json = JSON . parse ( data );
let price = json [ "result" ][ "b" ][ 0 ][ 0 ];
2023-03-29 02:37:31 +02:00
return price ;
}
async function getBitcoinPrice () {
let prices = [];
let cbprice = await getBitcoinPriceFromCoinbase ();
let kprice = await getBitcoinPriceFromKraken ();
let cdprice = await getBitcoinPriceFromCoindesk ();
let gprice = await getBitcoinPriceFromGemini ();
let bprice = await getBitcoinPriceFromBybit ();
2023-04-01 19:41:32 +02:00
prices . push ( Number ( cbprice ), Number ( kprice ), Number ( cdprice ), Number ( gprice ), Number ( bprice ));
2023-03-29 02:37:31 +02:00
prices . sort ();
2023-04-01 19:41:32 +02:00
return prices [ 2 ];
2023-03-29 02:37:31 +02:00
}
</ script >
</ head >
< body >
2023-04-01 19:41:32 +02:00
< h1 > Ordinals Inscriber</ h1 >
< h6 style = "margin-top: -15px;" > Self-custodial. A free service hosted by < a href = "https://rarity.garden" target = "_blank" > RarityGarden</ a > .</ h6 >
< h2 class = "safari_warning" > This tool may not work in safari browser. If you are using that, consider switching.</ h2 >
< div id = "setup" >
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
< div class = "mb-3" >
< label for = "taproot_address" > Receiving address < span class = "type_of_address" > (taproot)</ span ></ label >
< input id = "taproot_address" class = "address"
placeholder = "Enter a taproot address from an ordinals wallet" >
</ div >
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
< p > Please choose what you want to inscribe:</ p >
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
< ul >
< li >
< a id = "upload_file_nav" class = "upload_file active" href = "javascript:void(0);" > files</ a >
</ li >
< li >
< a id = "registration_nav" class = "registration" href = "javascript:void(0);" > .sats domains</ a >
</ li >
< li >
< a id = "brc20_mint_nav" class = "brc20_mint" href = "javascript:void(0);" > brc-20 mint</ a >
</ li >
< li class = "nav-item" >
< a id = "brc20_deploy_nav" class = "brc20_deploy" href = "javascript:void(0);" > brc-20 deploy</ a >
</ li >
</ ul >
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
< form id = "app-form" >
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
< div class = "file_form" >
< input
accept = ".json,
.pdf,
.asc,
.yaml,
.yml,
.flac,
.mp3,
.wav,
.apng,
.avif,
.gif,
.jpg,
.jpeg,
.png,
.svg,
.webp,
.glb,
.stl,
.html,
.txt,
.mp4,
.webm
" type = "file" class = "form" multiple >
2023-03-14 10:27:09 -05:00
2023-04-01 19:41:32 +02:00
< button style = "margin-top: 15px;" id = "bytes_checker" type = "button" class = "btn btn-outline-primary mb-3" >
Check if file(s) are inscribed already
</ button >
</ div >
< div class = "dns_form" >
< textarea class = "dns" style = "width: 100%; height: 250px;"
placeholder = "One domain per line. Example: ordi.sats btc.sats" ></ textarea >
</ div >
< div class = "brc20_mint_form" >
< div class = "mb-3" >
< label for = "brc20-mint-ticker" > Ticker</ label >
< input id = "brc20-mint-ticker" type = "text" maxlength = "4" value = ""
placeholder = "e.g. ordi" />
</ div >
< div class = "mb-3" >
< label for = "brc20-mint-amount" > Amount</ label >
< input id = "brc20-mint-amount" type = "text" value = "" placeholder = "e.g. 1000" />
</ div >
< div class = "mb-3" >
< label for = "brc20-mint-repeat" > Repeat</ label >
< input id = "brc20-mint-repeat" type = "text" value = "1"
placeholder = "e.g. 10 to mint ten times" />
</ div >
</ div >
< div class = "brc20_deploy_form" >
< div class = "mb-3" >
< label for = "brc20-deploy-ticker" > Ticker</ label >
< input id = "brc20-deploy-ticker" type = "text" maxlength = "4" value = ""
placeholder = "e.g. ordi" />
</ div >
< div class = "mb-3" >
< label for = "brc20-deploy-max" > Supply</ label >
< input id = "brc20-deploy-max" type = "text" value = "" placeholder = "e.g. 21000000" />
</ div >
< div class = "mb-3" >
< label for = "brc20-deploy-lim" > Limit per mint</ label >
< input id = "brc20-deploy-lim" type = "text" value = "" placeholder = "e.g. 1000" />
</ div >
</ div >
< button class = "dns_checker btn btn-outline-primary mt-3 mb-3" type = "button" > Check availability</ button >
< span class = "timer" > 61</ span >
< p > Select a feerate (sats/vB)</ p >
< div class = "feerates" >
< div class = "fee minfee" >
< div style = "color: white;" class = "num rounded" > ...</ div >
< div class = "name" > Min</ div >
</ div >
< div class = "fee midfee" >
< div style = "color: white;" class = "num rounded" > ...</ div >
< div class = "name" > Mid</ div >
</ div >
< div class = "fee maxfee" >
< div style = "color: white;" class = "num rounded" > ...</ div >
< div class = "name" > Max</ div >
2023-03-14 10:27:09 -05:00
</ div >
2023-03-29 02:37:31 +02:00
</ div >
2023-04-01 19:41:32 +02:00
< div id = "sliderange" >
< label for = "sats_range" > sats/vB: < span id = "sats_per_byte" > 1</ span ></ label >
< input class = "form-range" id = "sats_range" type = "range" min = "1" max = "100" value = "1" >
2023-03-29 02:37:31 +02:00
</ div >
2023-04-01 19:41:32 +02:00
< 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 >
< div class = "mb-3" >
< label for = "padding" > Sats in inscription (padding)</ label >
< input style = "width: 25%" id = "padding" type = "text" value = "" />
</ div >
</ div >
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
< div style = "margin-top: 20px; margin-bottom: 20px;" >
< button class = "submit btn btn-primary" > Submit</ button >
< button style = "display:none;" class = "startover btn btn-primary" > Reset</ button >
</ div >
< div class = "display" ></ div >
< div style = "display: none;" class = "file" ></ div >
2023-03-29 02:37:31 +02:00
< script >
// adjust for your needs
let padding = 10000 ; // default padding as of ord native wallet
let encodedAddressPrefix = 'bc' ; // mainnet: 'bc', signet/testnet: 'tb'
2023-04-01 19:41:32 +02:00
let mempoolNetwork = 'signet/' ; // mainnet: '', 'signet/', 'testnet/'
2023-03-29 02:37:31 +02:00
// no changes from here
2023-04-01 19:41:32 +02:00
const privkey = bytesToHex ( cryptoUtils . Noble . utils . randomPrivateKey ());
2023-03-29 02:37:31 +02:00
let pushing = false ;
let files = [];
sessionStorage . clear ();
2023-04-01 19:41:32 +02:00
window . onload = function () {
2023-03-29 02:37:31 +02:00
$ ( '#padding' ). value = padding ;
2023-04-01 19:41:32 +02:00
$ ( '.upload_file' ). onclick = showUploader ;
$ ( '.registration' ). onclick = showRegister ;
$ ( '.brc20_mint' ). onclick = showBrc20Mint ;
$ ( '.brc20_deploy' ). onclick = showBrc20Deploy ;
2023-03-29 02:37:31 +02:00
};
function showRegister () {
2023-03-29 16:31:54 +02:00
$ ( '#padding' ). value = '546' ;
padding = '546' ;
2023-03-29 02:37:31 +02:00
files = [];
$ ( '#app-form' ). reset ();
2023-04-01 19:41:32 +02:00
$ ( '.brc20_deploy_form' ). style . display = "none" ;
$ ( '.brc20_mint_form' ). style . display = "none" ;
$ ( '.file_form' ). style . display = "none" ;
$ ( '.dns_form' ). style . display = "block" ;
$ ( '.dns_checker' ). style . display = "inline" ;
$ ( '.dns' ). value = "" ;
2023-03-29 02:37:31 +02:00
document . getElementById ( 'brc20_mint_nav' ). classList . remove ( 'active' );
document . getElementById ( 'brc20_deploy_nav' ). classList . remove ( 'active' );
document . getElementById ( 'upload_file_nav' ). classList . remove ( 'active' );
document . getElementById ( 'registration_nav' ). classList . add ( 'active' );
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
function showUploader () {
2023-03-29 16:31:54 +02:00
$ ( '#padding' ). value = '10000' ;
padding = '10000' ;
2023-03-29 02:37:31 +02:00
files = [];
$ ( '#app-form' ). reset ();
2023-04-01 19:41:32 +02:00
$ ( '.brc20_deploy_form' ). style . display = "none" ;
$ ( '.brc20_mint_form' ). style . display = "none" ;
$ ( '.file_form' ). style . display = "block" ;
$ ( '.dns_form' ). style . display = "none" ;
$ ( '.dns_checker' ). style . display = "none" ;
2023-03-29 02:37:31 +02:00
document . getElementById ( 'brc20_mint_nav' ). classList . remove ( 'active' );
document . getElementById ( 'brc20_deploy_nav' ). classList . remove ( 'active' );
document . getElementById ( 'upload_file_nav' ). classList . add ( 'active' );
document . getElementById ( 'registration_nav' ). classList . remove ( 'active' );
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
function showBrc20Deploy () {
2023-03-29 16:31:54 +02:00
$ ( '#padding' ). value = '546' ;
padding = '546' ;
2023-03-29 02:37:31 +02:00
files = [];
$ ( '#app-form' ). reset ();
2023-04-01 19:41:32 +02:00
$ ( '.brc20_deploy_form' ). style . display = "block" ;
$ ( '.brc20_mint_form' ). style . display = "none" ;
$ ( '.file_form' ). style . display = "none" ;
$ ( '.dns_form' ). style . display = "none" ;
$ ( '.dns_checker' ). style . display = "none" ;
$ ( '.registration' ). onclick = showRegister ;
2023-03-29 02:37:31 +02:00
document . getElementById ( 'brc20_mint_nav' ). classList . remove ( 'active' );
document . getElementById ( 'brc20_deploy_nav' ). classList . add ( 'active' );
document . getElementById ( 'upload_file_nav' ). classList . remove ( 'active' );
document . getElementById ( 'registration_nav' ). classList . remove ( 'active' );
}
function showBrc20Mint () {
2023-03-29 16:31:54 +02:00
$ ( '#padding' ). value = '546' ;
padding = '546' ;
2023-03-29 02:37:31 +02:00
files = [];
$ ( '#app-form' ). reset ();
2023-04-01 19:41:32 +02:00
$ ( '.brc20_deploy_form' ). style . display = "none" ;
$ ( '.brc20_mint_form' ). style . display = "block" ;
$ ( '.file_form' ). style . display = "none" ;
$ ( '.dns_form' ). style . display = "none" ;
$ ( '.dns_checker' ). style . display = "none" ;
$ ( '.registration' ). onclick = showRegister ;
2023-03-29 02:37:31 +02:00
document . getElementById ( 'brc20_mint_nav' ). classList . add ( 'active' );
document . getElementById ( 'brc20_deploy_nav' ). classList . remove ( 'active' );
document . getElementById ( 'upload_file_nav' ). classList . remove ( 'active' );
document . getElementById ( 'registration_nav' ). classList . remove ( 'active' );
}
showUploader ();
2023-04-01 19:41:32 +02:00
$ ( '.form' ). addEventListener ( "change" , async function () {
2023-03-29 02:37:31 +02:00
files = [];
let limit_reached = 0 ;
2023-04-01 19:41:32 +02:00
for ( let i = 0 ; i < this . files . length ; i ++ ) {
2023-03-29 02:37:31 +02:00
let b64 ;
let mimetype = this . files [ i ]. type ;
2023-04-01 19:41:32 +02:00
if ( mimetype . includes ( "text/plain" )) {
2023-03-29 02:37:31 +02:00
mimetype += ";charset=utf-8" ;
2023-03-16 12:33:19 -05:00
}
2023-03-29 02:37:31 +02:00
if ( this . files [ i ]. size >= 350000 ) {
limit_reached += 1 ;
} else {
2023-04-01 19:41:32 +02:00
b64 = await encodeBase64 ( this . files [ i ]);
let base64 = b64 . substring ( b64 . indexOf ( "base64," ) + 7 );
let hex = base64ToHex ( base64 );
2023-03-29 02:37:31 +02:00
//console.log( "hex:", hex );
//console.log( "bytes:", hexToBytes( hex ) );
let sha256 = await fileToSha256Hex ( this . files [ i ]);
2023-04-01 19:41:32 +02:00
files . push ({ name : this . files [ i ]. name , hex : hex , mimetype : mimetype , sha256 : sha256 . replace ( '0x' , '' )});
2023-03-16 12:33:19 -05:00
}
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
if ( limit_reached != 0 ) {
alert ( limit_reached + " of your desired inscriptions exceed(s) the maximum of 350kb." )
2023-03-29 02:37:31 +02:00
}
console . log ( files );
});
2023-04-01 19:41:32 +02:00
$ ( '.startover' ). addEventListener ( "click" , async function () {
2023-03-29 02:37:31 +02:00
location . reload ();
});
2023-04-01 19:41:32 +02:00
$ ( '.submit' ). addEventListener ( "click" , async function () {
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
if ( ! isValidAddress ()) {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid taproot address.' );
return ;
}
2023-04-01 19:41:32 +02:00
if ( $ ( '.brc20_deploy_form' ). style . display != "none" ) {
2023-03-29 02:37:31 +02:00
files = [];
let deploy = '{ \n' +
' "p": "brc-20",\n' +
' "op": "deploy",\n' +
' "tick": "",\n' +
' "max": "",\n' +
' "lim": ""\n' +
'}' ;
2023-04-01 19:41:32 +02:00
if ( isNaN ( parseInt ( $ ( '#brc20-deploy-max' ). value ))) {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid supply.' );
2023-03-14 10:27:09 -05:00
return ;
}
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
if ( isNaN ( parseInt ( $ ( '#brc20-deploy-lim' ). value ))) {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid limit.' );
return ;
2023-03-14 10:27:09 -05:00
}
2023-04-01 19:41:32 +02:00
if ( $ ( '#brc20-deploy-ticker' ). value == '' || $ ( '#brc20-deploy-ticker' ). value . length != 4 ) {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid ticker length. Must be 4 characters.' );
return ;
}
deploy = JSON . parse ( deploy );
deploy . tick = $ ( '#brc20-deploy-ticker' ). value ;
deploy . max = $ ( '#brc20-deploy-max' ). value ;
deploy . lim = $ ( '#brc20-deploy-lim' ). value ;
let mimetype = "text/plain;charset=utf-8" ;
2023-04-01 19:41:32 +02:00
files . push ({ name : deploy . tick , hex : textToHex ( JSON . stringify ( deploy )), mimetype : mimetype , sha256 : '' });
2023-03-29 02:37:31 +02:00
console . log ( files );
}
2023-04-01 19:41:32 +02:00
if ( $ ( '.brc20_mint_form' ). style . display != "none" ) {
2023-03-29 02:37:31 +02:00
files = [];
let mint = '{ \n' +
' "p": "brc-20",\n' +
' "op": "mint",\n' +
' "tick": "",\n' +
' "amt": ""\n' +
'}' ;
2023-04-01 19:41:32 +02:00
if ( isNaN ( parseInt ( $ ( '#brc20-mint-amount' ). value ))) {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid mint amount.' );
return ;
}
2023-04-01 19:41:32 +02:00
if ( $ ( '#brc20-mint-ticker' ). value == '' || $ ( '#brc20-mint-ticker' ). value . length != 4 ) {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid ticker length. Must be 4 characters.' );
return ;
}
mint = JSON . parse ( mint );
mint . tick = $ ( '#brc20-mint-ticker' ). value ;
mint . amt = $ ( '#brc20-mint-amount' ). value ;
let repeat = parseInt ( $ ( '#brc20-mint-repeat' ). value );
2023-04-01 19:41:32 +02:00
if ( isNaN ( repeat )) {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid repeat amount.' );
return ;
}
2023-04-01 19:41:32 +02:00
for ( let i = 0 ; i < repeat ; i ++ ) {
2023-03-29 02:37:31 +02:00
let mimetype = "text/plain;charset=utf-8" ;
2023-04-01 19:41:32 +02:00
files . push ({
name : mint . tick + '_' + i ,
hex : textToHex ( JSON . stringify ( mint )),
mimetype : mimetype ,
sha256 : ''
});
2023-03-29 02:37:31 +02:00
}
console . log ( files );
}
2023-04-01 19:41:32 +02:00
if ( $ ( '.dns_form' ). style . display != "none" ) {
2023-03-29 02:37:31 +02:00
files = [];
2023-04-01 19:41:32 +02:00
let sats_domains = $ ( '.dns' ). value . split ( "\n" );
2023-03-29 02:37:31 +02:00
let sats_domains_cleaned = [];
2023-04-01 19:41:32 +02:00
for ( let sats_domain in sats_domains ) {
2023-03-29 02:37:31 +02:00
let domain = sats_domains [ sats_domain ]. trim ();
if ( domain == '' || sats_domains_cleaned . includes ( domain )) {
continue ;
}
sats_domains_cleaned . push ( domain );
}
2023-04-01 19:41:32 +02:00
for ( let sats_domain in sats_domains_cleaned ) {
2023-03-29 02:37:31 +02:00
let mimetype = "text/plain;charset=utf-8" ;
2023-04-01 19:41:32 +02:00
let domain = { "p" : "sns" , "op" : "reg" , "name" : sats_domains_cleaned [ sats_domain ]. trim ()};
files . push ({
name : sats_domains_cleaned [ sats_domain ]. trim (),
hex : textToHex ( JSON . stringify ( domain )),
mimetype : mimetype ,
sha256 : ''
});
2023-03-29 02:37:31 +02:00
console . log ( domain );
}
}
2023-04-01 19:41:32 +02:00
if ( files . length == 0 ) {
2023-03-29 02:37:31 +02:00
alert ( 'Nothing to inscribe. Please upload some files or use one of the additional options.' );
return ;
}
let _padding = parseInt ( $ ( '#padding' ). value );
2023-04-01 19:41:32 +02:00
if ( ! isNaN ( _padding ) && _padding <= Number . MAX_SAFE_INTEGER && _padding >= 1 ) {
2023-03-29 02:37:31 +02:00
padding = _padding ;
2023-04-01 19:41:32 +02:00
} else {
2023-03-29 02:37:31 +02:00
alert ( 'Invalid padding. Please enter a sats amount for each inscription.' );
return ;
}
2023-04-01 19:41:32 +02:00
$ ( '.submit' ). style . display = "none" ;
$ ( '.startover' ). style . display = "inline-block" ;
2023-03-29 02:37:31 +02:00
const KeyPair = cryptoUtils . KeyPair ;
const seckey = new KeyPair ( privkey );
2023-04-01 19:41:32 +02:00
const pubkey = seckey . pub . rawX ;
2023-03-29 02:37:31 +02:00
const ec = new TextEncoder ();
const init_script = [
pubkey ,
'OP_CHECKSIG'
];
2023-04-01 19:41:32 +02:00
const init_leaf = await BTON . Tap . getLeaf ( BTON . Script . encode ( init_script ));
const [ init_tapkey ] = await BTON . Tap . getPubkey ( pubkey , [ init_leaf ]);
const init_cblock = await BTON . Tap . getPath ( pubkey , init_leaf );
2023-03-29 02:37:31 +02:00
let inscriptions = [];
let total_fee = 0 ;
let feerate = await getMinFeeRate ();
2023-04-01 19:41:32 +02:00
sessionStorage [ "determined_feerate" ] = sessionStorage [ "feerate" ];
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
if ( sessionStorage [ "feerate" ]) {
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
feerate = Number ( sessionStorage [ "feerate" ]);
sessionStorage [ "determined_feerate" ] = sessionStorage [ "feerate" ];
2023-03-29 02:37:31 +02:00
}
2023-03-29 15:56:40 +02:00
let base_size = 160 ;
2023-04-01 19:41:32 +02:00
for ( let i = 0 ; i < files . length ; i ++ ) {
2023-03-29 02:37:31 +02:00
const hex = files [ i ]. hex ;
2023-04-01 19:41:32 +02:00
const data = hexToBytes ( hex );
const mimetype = ec . encode ( files [ i ]. mimetype );
2023-03-29 02:37:31 +02:00
const script = [
pubkey ,
'OP_CHECKSIG' ,
'OP_0' ,
'OP_IF' ,
ec . encode ( 'ord' ),
'01' ,
mimetype ,
'OP_0' ,
data ,
'OP_ENDIF'
];
2023-04-01 19:41:32 +02:00
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 );
let inscriptionAddress = BTON . Tap . encodeAddress ( tapkey , encodedAddressPrefix );
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
console . log ( 'Inscription address: ' , inscriptionAddress );
2023-03-29 02:37:31 +02:00
console . log ( 'Tapkey:' , tapkey );
2023-03-29 05:08:35 +02:00
let txsize = 600 + Math . floor ( data . length / 4 );
if ( files [ i ]. sha256 == '' )
{
2023-03-29 15:56:40 +02:00
base_size = Math . floor ( data . length / 4 ) * i ;
txsize = 200 + Math . floor ( data . length / 4 );
2023-03-29 05:08:35 +02:00
}
2023-03-29 02:37:31 +02:00
2023-03-29 15:56:40 +02:00
console . log ( "TXSIZE" , txsize );
2023-03-29 02:37:31 +02:00
let fee = feerate * txsize ;
total_fee += fee ;
inscriptions . push (
{
leaf : leaf ,
2023-04-01 19:41:32 +02:00
tapkey : tapkey ,
cblock : cblock ,
inscriptionAddress : inscriptionAddress ,
txsize : txsize ,
fee : fee ,
script : script
2023-03-29 02:37:31 +02:00
}
);
}
2023-04-01 19:41:32 +02:00
let fundingAddress = BTON . Tap . encodeAddress ( init_tapkey , encodedAddressPrefix );
console . log ( 'Funding address: ' , fundingAddress );
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
let toAddress = $ ( '.address' ). value ;
console . log ( 'Address that will receive the inscription:' , toAddress );
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
let decodedToAddress = "5120" + BTON . Tap . decodeAddress ( toAddress ). hex ;
console . log ( 'To address decoded:' , decodedToAddress );
2023-03-29 02:37:31 +02:00
2023-03-29 15:56:40 +02:00
let total_fees = 550 + ( base_size * inscriptions . length ) + total_fee + ( padding * inscriptions . length );
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
let sats_price = await satsToDollars ( total_fees );
sats_price = Math . floor ( sats_price * 100 ) / 100 ;
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
let html = `<p>Please send at least <strong> ${ total_fees } sats</strong> ($ ${ sats_price } ) to the address below (click to copy). Once you sent the amount, do NOT close this window!</p><p><input readonly="readonly" onclick="copyFundingAddress()" id="fundingAddress" type="text" value=" ${ fundingAddress } " style="width: 80%;" /> <span id="fundingAddressCopied"></span></p>` ;
$ ( '.display' ). innerHTML = html ;
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
let qr_value = "bitcoin:" + fundingAddress + "?amount=" + satsToBitcoin ( total_fees );
console . log ( "qr:" , qr_value );
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
$ ( '.display' ). append ( createQR ( qr_value ));
$ ( '.display' ). innerHTML += `<p class="checking_mempool">Checking the mempool<span class="dots">.</span></p>` ;
$ ( '.display' ). innerHTML += '<p>' + ( padding * inscriptions . length ) + ` sats will go to the address</p><p> ${ total_fee } sats will go to miners as a mining fee</p>` ;
$ ( '.display' ). style . display = "block" ;
2023-03-29 02:37:31 +02:00
$ ( '#setup' ). style . display = "none" ;
2023-04-01 19:41:32 +02:00
await loopTilAddressReceivesMoney ( fundingAddress , true );
await waitSomeSeconds ( 2 );
let txinfo = await addressReceivedMoneyInThisTx ( fundingAddress );
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
let txid = txinfo [ 0 ];
let vout = txinfo [ 1 ];
let amt = txinfo [ 2 ];
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
console . log ( "yay! txid:" , txid , "vout:" , vout , "amount:" , amt );
2023-03-29 02:37:31 +02:00
$ ( '.modal-content' ). innerHTML = '<div id="funds-msg">Funds are on the way. Please wait...</div>' ;
2023-04-01 19:41:32 +02:00
$ ( '.modal' ). style . display = "block" ;
2023-03-29 02:37:31 +02:00
let outputs = [];
2023-04-01 19:41:32 +02:00
for ( let i = 0 ; i < inscriptions . length ; i ++ ) {
2023-03-29 02:37:31 +02:00
outputs . push (
{
value : padding + inscriptions [ i ]. fee ,
scriptPubKey : '5120' + inscriptions [ i ]. tapkey
}
);
}
const init_redeemtx = {
version : 2 ,
input : [{
txid : txid ,
vout : vout ,
2023-04-01 19:41:32 +02:00
prevout : { value : amt , scriptPubKey : '5120' + init_tapkey },
2023-03-29 02:37:31 +02:00
witness : []
}],
output : outputs ,
locktime : 0
};
2023-04-01 19:41:32 +02:00
const init_sig = await BTON . Sig . taproot . sign ( seckey . raw , init_redeemtx , 0 , { extention : init_leaf });
init_redeemtx . input [ 0 ]. witness = [ init_sig , init_script , init_cblock ];
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
console . dir ( init_redeemtx , { depth : null });
console . log ( 'YOUR SECKEY' , seckey );
2023-03-29 02:37:31 +02:00
let rawtx = BTON . Tx . encode ( init_redeemtx );
2023-04-01 19:41:32 +02:00
let _txid = await pushBTCpmt ( rawtx );
2023-03-29 02:37:31 +02:00
console . log ( 'Init TX' , _txid );
2023-03-29 15:56:40 +02:00
let include_mempool = true ;
2023-03-29 07:12:08 +02:00
2023-04-01 19:41:32 +02:00
async function inscribe ( inscription , vout ) {
2023-03-29 02:37:31 +02:00
2023-03-29 07:12:08 +02:00
// we are running into an issue with 25 child transactions for unconfirmed parents.
// so once the limit is reached, we wait for the parent tx to confirm.
await loopTilAddressReceivesMoney ( inscription . inscriptionAddress , include_mempool );
2023-04-01 19:41:32 +02:00
await waitSomeSeconds ( 2 );
let txinfo2 = await addressReceivedMoneyInThisTx ( inscription . inscriptionAddress );
2023-03-29 02:37:31 +02:00
document . getElementById ( 'modal-reset' ). style . display = 'block' ;
document . getElementById ( 'funds-msg' ). style . display = 'none' ;
2023-04-01 19:41:32 +02:00
let txid2 = txinfo2 [ 0 ];
let amt2 = txinfo2 [ 2 ];
2023-03-29 02:37:31 +02:00
const redeemtx = {
version : 2 ,
input : [{
txid : txid2 ,
vout : vout ,
2023-04-01 19:41:32 +02:00
prevout : { value : amt2 , scriptPubKey : '5120' + inscription . tapkey },
2023-03-29 02:37:31 +02:00
witness : []
}],
2023-04-01 19:41:32 +02:00
output : [{
2023-03-29 02:37:31 +02:00
value : amt2 - inscription . fee ,
scriptPubKey : decodedToAddress
}],
locktime : 0
};
2023-04-01 19:41:32 +02:00
const sig = await BTON . Sig . taproot . sign ( seckey . raw , redeemtx , 0 , { extention : inscription . leaf });
redeemtx . input [ 0 ]. witness = [ sig , inscription . script , inscription . cblock ];
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
console . dir ( redeemtx , { depth : null });
2023-03-29 02:37:31 +02:00
let rawtx2 = BTON . Tx . encode ( redeemtx );
2023-03-29 12:42:25 +02:00
let _txid2 ;
2023-03-29 02:37:31 +02:00
// since we don't know any mempool space api rate limits, we will be careful with spamming
await isPushing ();
pushing = true ;
2023-03-29 15:56:40 +02:00
_txid2 = await pushBTCpmt ( rawtx2 );
2023-03-29 02:37:31 +02:00
await sleep ( 1000 );
pushing = false ;
2023-03-29 15:56:40 +02:00
if ( _txid2 . includes ( 'descendant' ))
{
include_mempool = false ;
inscribe ( inscription , vout );
$ ( '#descendants-warning' ). style . display = 'inline-block' ;
return ;
}
2023-04-01 19:41:32 +02:00
try {
2023-03-29 02:37:31 +02:00
JSON . parse ( _txid2 );
let html = `<p style="background-color: white; color: black;">Error: ${ _txid2 } </p>` ;
html += '<hr/>' ;
2023-04-01 19:41:32 +02:00
$ ( '.modal' ). innerHTML += html ;
} catch ( e ) {
2023-03-29 02:37:31 +02:00
let html = `<p style="background-color: white; color: black;">Inscription # ${ vout } transaction:</p><p style="word-wrap: break-word;"><a href="https://mempool.space/ ${ mempoolNetwork } tx/ ${ _txid2 } " target="_blank">https://mempool.space/ ${ mempoolNetwork } tx/ ${ _txid2 } </a></p>` ;
html += `<p style="background-color: white; color: black;">Ordinals explorer (after tx confirmation):</p><p style="word-wrap: break-word;"><a href="https://ordinals.com/inscription/ ${ _txid2 } i0" target="_blank">https://ordinals.com/inscription/ ${ _txid2 } i0</a></p>` ;
html += '<hr/>' ;
2023-04-01 19:41:32 +02:00
$ ( '.modal-content' ). innerHTML += html ;
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
$ ( '.modal' ). style . display = "block" ;
$ ( '.black-bg' ). style . display = "block" ;
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
for ( let i = 0 ; i < inscriptions . length ; i ++ ) {
2023-03-29 02:37:31 +02:00
inscribe ( inscriptions [ i ], i );
}
});
function arrayBufferToBuffer ( ab ) {
var buffer = new buf . Buffer ( ab . byteLength )
var view = new Uint8Array ( ab )
for ( var i = 0 ; i < buffer . length ; ++ i ) {
buffer [ i ] = view [ i ]
}
return buffer
}
function hexString ( buffer ) {
const byteArray = new Uint8Array ( buffer )
const hexCodes = [... byteArray ]. map ( value => {
return value . toString ( 16 ). padStart ( 2 , '0' )
})
2023-04-01 19:41:32 +02:00
return '0x' + hexCodes . join ( '' )
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
async function fileToArrayBuffer ( file ) {
2023-03-29 02:37:31 +02:00
return new Promise ( function ( resolve , reject ) {
const reader = new FileReader ()
2023-04-01 19:41:32 +02:00
const readFile = function ( event ) {
2023-03-29 02:37:31 +02:00
const buffer = reader . result
resolve ( buffer )
}
reader . addEventListener ( 'load' , readFile )
reader . readAsArrayBuffer ( file )
})
}
2023-04-01 19:41:32 +02:00
async function bufferToSha256 ( buffer ) {
2023-03-29 02:37:31 +02:00
return window . crypto . subtle . digest ( 'SHA-256' , buffer )
}
2023-04-01 19:41:32 +02:00
async function fileToSha256Hex ( file ) {
2023-03-29 02:37:31 +02:00
const buffer = await fileToArrayBuffer ( file )
const hash = await bufferToSha256 ( arrayBufferToBuffer ( buffer ))
return hexString ( hash )
}
function copyFundingAddress () {
let copyText = document . getElementById ( "fundingAddress" );
copyText . select ();
copyText . setSelectionRange ( 0 , 99999 );
navigator . clipboard . writeText ( copyText . value );
document . getElementById ( "fundingAddressCopied" ). innerHTML = ' Copied!' ;
2023-04-01 19:41:32 +02:00
setTimeout ( function () {
2023-03-29 02:37:31 +02:00
document . getElementById ( "fundingAddressCopied" ). innerHTML = '' ;
}, 5000 );
}
2023-04-01 19:41:32 +02:00
async function isPushing () {
while ( pushing ) {
2023-03-29 02:37:31 +02:00
await sleep ( 10 );
}
}
function sleep ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ));
}
async function getMaxFeeRate () {
2023-04-01 19:41:32 +02:00
let fees = await getData ( "https://mempool.space/" + mempoolNetwork + "api/v1/fees/recommended" );
fees = JSON . parse ( fees );
2023-03-29 02:37:31 +02:00
// if ( !( "minimumFee" in fees ) ) return "error -- site down";
// var minfee = fees[ "minimumFee" ];
2023-04-01 19:41:32 +02:00
if ( ! ( "fastestFee" in fees )) return "error -- site down" ;
let maxfee = fees [ "fastestFee" ];
2023-03-29 02:37:31 +02:00
return maxfee ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
async function getMinFeeRate () {
2023-04-01 19:41:32 +02:00
let fees = await getData ( "https://mempool.space/" + mempoolNetwork + "api/v1/fees/recommended" );
fees = JSON . parse ( fees );
if ( ! ( "minimumFee" in fees )) return "error -- site down" ;
let minfee = fees [ "minimumFee" ];
2023-03-29 02:37:31 +02:00
// if ( !( "fastestFee" in fees ) ) return "error -- site down";
// var maxfee = fees[ "fastestFee" ];
return minfee ;
}
2023-04-01 19:41:32 +02:00
function isValidTaprootAddress ( address ) {
2023-03-29 02:37:31 +02:00
try {
2023-04-01 19:41:32 +02:00
BTON . Tap . decodeAddress ( address ). hex ;
2023-03-29 02:37:31 +02:00
return true ;
2023-04-01 19:41:32 +02:00
} catch ( e ) {
}
2023-03-29 02:37:31 +02:00
return ;
}
2023-04-01 19:41:32 +02:00
function isValidJson ( content ) {
if ( ! content ) return ;
2023-03-29 02:37:31 +02:00
try {
2023-04-01 19:41:32 +02:00
var json = JSON . parse ( content );
} catch ( e ) {
2023-03-29 02:37:31 +02:00
return ;
}
return true ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
async function getAllFeeRates () {
2023-04-01 19:41:32 +02:00
let fees = await getData ( "https://mempool.space/" + mempoolNetwork + "api/v1/fees/recommended" );
fees = JSON . parse ( fees );
2023-03-29 02:37:31 +02:00
return fees ;
}
2023-04-01 19:41:32 +02:00
function getData ( url ) {
return new Promise ( async function ( resolve , reject ) {
function inner_get ( url ) {
2023-03-29 02:37:31 +02:00
let xhttp = new XMLHttpRequest ();
2023-04-01 19:41:32 +02:00
xhttp . open ( "GET" , url , true );
2023-03-29 02:37:31 +02:00
xhttp . send ();
return xhttp ;
}
2023-04-01 19:41:32 +02:00
let data = inner_get ( url );
data . onerror = function ( e ) {
resolve ( "error" );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
async function isResponseReady () {
2023-04-01 19:41:32 +02:00
return new Promise ( function ( resolve2 , reject ) {
if ( ! data . responseText || data . readyState != 4 ) {
setTimeout ( async function () {
2023-03-29 02:37:31 +02:00
let msg = await isResponseReady ();
2023-04-01 19:41:32 +02:00
resolve2 ( msg );
}, 1 );
2023-03-29 02:37:31 +02:00
} else {
2023-04-01 19:41:32 +02:00
resolve2 ( data . responseText );
2023-03-29 02:37:31 +02:00
}
});
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
let returnable = await isResponseReady ();
2023-04-01 19:41:32 +02:00
resolve ( returnable );
2023-03-29 02:37:31 +02:00
});
}
2023-04-01 19:41:32 +02:00
async function pushBTCpmt ( rawtx ) {
let txid = await postData ( "https://mempool.space/" + mempoolNetwork + "api/tx" , rawtx );
2023-03-29 02:37:31 +02:00
return txid ;
}
2023-04-01 19:41:32 +02:00
function waitSomeSeconds ( number ) {
2023-03-29 02:37:31 +02:00
let num = number . toString () + "000" ;
2023-04-01 19:41:32 +02:00
num = Number ( num );
return new Promise ( function ( resolve , reject ) {
setTimeout ( function () {
resolve ( "" );
}, num );
2023-03-29 02:37:31 +02:00
});
}
2023-04-01 19:41:32 +02:00
async function postData ( url , json , content_type = "" , apikey = "" ) {
2023-03-29 02:37:31 +02:00
let rtext = "" ;
2023-04-01 19:41:32 +02:00
function inner_post ( url , json , content_type = "" , apikey = "" ) {
2023-03-29 02:37:31 +02:00
let xhttp = new XMLHttpRequest ();
2023-04-01 19:41:32 +02:00
xhttp . open ( "POST" , url , true );
if ( content_type ) {
xhttp . setRequestHeader ( `Content-Type` , content_type );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
if ( apikey ) {
xhttp . setRequestHeader ( `X-Api-Key` , apikey );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
xhttp . send ( json );
2023-03-29 02:37:31 +02:00
return xhttp ;
}
2023-04-01 19:41:32 +02:00
let data = inner_post ( url , json , content_type , apikey );
data . onerror = function ( e ) {
2023-03-29 02:37:31 +02:00
rtext = "error" ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
async function isResponseReady () {
2023-04-01 19:41:32 +02:00
return new Promise ( function ( resolve , reject ) {
if ( rtext == "error" ) {
resolve ( rtext );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
if ( ! data . responseText || data . readyState != 4 ) {
setTimeout ( async function () {
2023-03-29 02:37:31 +02:00
let msg = await isResponseReady ();
2023-04-01 19:41:32 +02:00
resolve ( msg );
}, 50 );
2023-03-29 02:37:31 +02:00
} else {
2023-04-01 19:41:32 +02:00
resolve ( data . responseText );
2023-03-29 02:37:31 +02:00
}
});
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
let returnable = await isResponseReady ();
return returnable ;
}
2023-04-01 19:41:32 +02:00
async function loopTilAddressReceivesMoney ( address , includeMempool ) {
2023-03-29 02:37:31 +02:00
let itReceivedMoney = false ;
2023-04-01 19:41:32 +02:00
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..." );
2023-03-29 15:56:40 +02:00
try {
itReceivedMoney = await addressOnceHadMoney ( address , includeMempool );
} catch ( e ){ }
2023-04-01 19:41:32 +02:00
let msg = await isDataSetYet ( itReceivedMoney );
resolve ( msg );
}, 2000 );
2023-03-29 02:37:31 +02:00
} else {
2023-04-01 19:41:32 +02:00
resolve ( data_i_seek );
2023-03-29 02:37:31 +02:00
}
});
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
async function getTimeoutData () {
2023-04-01 19:41:32 +02:00
let data_i_seek = await isDataSetYet ( itReceivedMoney );
2023-03-29 02:37:31 +02:00
return data_i_seek ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
let returnable = await getTimeoutData ();
return returnable ;
}
2023-04-01 19:41:32 +02:00
async function addressReceivedMoneyInThisTx ( address ) {
2023-03-29 02:37:31 +02:00
let txid ;
let vout ;
let amt ;
2023-04-01 19:41:32 +02:00
let nonjson = await getData ( "https://mempool.space/" + mempoolNetwork + "api/address/" + address + "/txs" );
let json = JSON . parse ( nonjson );
json . forEach ( function ( tx ) {
tx [ "vout" ]. forEach ( function ( output , index ) {
if ( output [ "scriptpubkey_address" ] == address ) {
txid = tx [ "txid" ];
2023-03-14 10:27:09 -05:00
vout = index ;
2023-04-01 19:41:32 +02:00
amt = output [ "value" ];
2023-03-14 10:27:09 -05:00
}
});
2023-03-29 02:37:31 +02:00
});
return [ txid , vout , amt ];
}
2023-04-01 19:41:32 +02:00
async function addressOnceHadMoney ( address , includeMempool ) {
let url = "https://mempool.space/" + mempoolNetwork + "api/address/" + address ;
let nonjson = await getData ( url );
if ( ! isValidJson ( nonjson )) return false ;
let json = JSON . parse ( nonjson );
if ( json [ "chain_stats" ][ "tx_count" ] > 0 || ( includeMempool && json [ "mempool_stats" ][ "tx_count" ] > 0 )) {
2023-03-29 02:37:31 +02:00
return true ;
}
return false ;
}
2023-04-01 19:41:32 +02:00
function dotLoop ( string ) {
if ( ! $ ( '.dots' )) {
setTimeout ( function () {
dotLoop ( string );
}, 1000 );
2023-03-29 02:37:31 +02:00
return ;
}
2023-04-01 19:41:32 +02:00
if ( string . length < 3 ) {
2023-03-29 02:37:31 +02:00
string = string + "." ;
} else {
string = "." ;
}
2023-04-01 19:41:32 +02:00
$ ( '.dots' ). innerText = string ;
setTimeout ( function () {
dotLoop ( string );
}, 1000 );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
dotLoop ( "." );
function timer ( num ) {
if ( ! num ) {
$ ( '.timer' ). style . display = "none" ;
2023-03-29 02:37:31 +02:00
return ;
}
num = num - 1 ;
2023-04-01 19:41:32 +02:00
$ ( '.timer' ). innerText = num ;
setTimeout ( function () {
timer ( num );
}, 1000 );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
function satsToBitcoin ( sats ) {
if ( sats >= 100000000 ) sats = sats * 10 ;
let string = String ( sats ). padStart ( 8 , "0" ). slice ( 0 , - 9 ) + "." + String ( sats ). padStart ( 8 , "0" ). slice ( - 9 );
if ( string . substring ( 0 , 1 ) == "." ) string = "0" + string ;
2023-03-29 02:37:31 +02:00
return string ;
}
2023-04-01 19:41:32 +02:00
async function satsToDollars ( sats ) {
if ( sats >= 100000000 ) sats = sats * 10 ;
let bitcoin_price = sessionStorage [ "bitcoin_price" ];
let value_in_dollars = Number ( String ( sats ). padStart ( 8 , "0" ). slice ( 0 , - 9 ) + "." + String ( sats ). padStart ( 8 , "0" ). slice ( - 9 )) * bitcoin_price ;
2023-03-29 02:37:31 +02:00
return value_in_dollars ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
function modalVanish () {
2023-04-01 19:41:32 +02:00
$ ( ".black-bg" ). style . display = "none" ;
$ ( ".modal" ). style . display = "none" ;
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
$$ ( '.fee' ). forEach ( function ( item ) {
item . onclick = function () {
$$ ( '.fee .num' ). forEach ( function ( item2 ) {
2023-03-29 02:37:31 +02:00
item2 . style . backgroundColor = "grey" ;
});
2023-04-01 19:41:32 +02:00
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 );
2023-03-29 02:37:31 +02:00
}
});
function isValidAddress () {
2023-04-01 19:41:32 +02:00
if ( ! isValidTaprootAddress ( $ ( '.address' ). value )) {
2023-03-29 02:37:31 +02:00
return false ;
}
return true ;
}
function checkAddress () {
2023-04-01 19:41:32 +02:00
if ( ! isValidAddress ()) {
$ ( '.address' ). style . backgroundColor = "#ff5252" ;
$ ( '.address' ). style . border = "2px solid red" ;
$ ( '.type_of_address' ). style . border = "1px solid white" ;
2023-03-29 02:37:31 +02:00
} else {
2023-04-01 19:41:32 +02:00
$ ( '.address' ). style . backgroundColor = "initial" ;
$ ( '.address' ). style . border = "1px solid white" ;
$ ( '.type_of_address' ). style . borderStyle = "none" ;
2023-03-29 02:37:31 +02:00
}
}
2023-04-01 19:41:32 +02:00
$ ( '.address' ). onchange = checkAddress ;
$ ( '.address' ). onpaste = checkAddress ;
$ ( '.address' ). onkeyup = checkAddress ;
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
async function isUsedDomain ( domain ) {
let data = await getData ( `https://api.sats.id/names/ ${ domain } ` );
console . log ( "data:" , data );
data = JSON . parse ( data );
console . log ( "data:" , data );
if ( "name" in data ) return true ;
if ( data [ "error" ] == "Too many requests" ) return null ;
2023-03-29 02:37:31 +02:00
return false ;
}
2023-04-01 19:41:32 +02:00
2023-03-29 02:37:31 +02:00
async function checkDomain () {
$ ( '.dns_checker' ). innerHTML = 'Please wait...' ;
let i = 1 ;
let registered = [];
let rate_limited = false ;
2023-04-01 19:41:32 +02:00
let sats_domains = $ ( '.dns' ). value . split ( "\n" );
2023-03-29 02:37:31 +02:00
let sats_domains_cleaned = [];
2023-04-01 19:41:32 +02:00
for ( let sats_domain in sats_domains ) {
2023-03-29 02:37:31 +02:00
let domain = sats_domains [ sats_domain ]. trim ();
if ( domain == '' || sats_domains_cleaned . includes ( domain )) {
continue ;
}
sats_domains_cleaned . push ( domain );
}
2023-04-01 19:41:32 +02:00
for ( let sats_domain in sats_domains_cleaned ) {
2023-03-29 02:37:31 +02:00
let domain = sats_domains_cleaned [ sats_domain ]. trim ();
2023-04-01 19:41:32 +02:00
$ ( '.dns_checker' ). innerHTML = 'Checking...(' + i + '/' + sats_domains_cleaned . length + ')' ;
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
let isUsed = await isUsedDomain ( domain );
2023-03-29 02:37:31 +02:00
2023-04-01 19:41:32 +02:00
if ( domain && isUsed === true ) {
2023-03-29 02:37:31 +02:00
registered . push ( domain );
2023-04-01 19:41:32 +02:00
} else if ( domain && isUsed === null ) {
2023-03-29 02:37:31 +02:00
rate_limited = true ;
break ;
}
await sleep ( 1000 );
i ++ ;
}
$ ( '.dns_checker' ). innerHTML = 'Check availability' ;
2023-04-01 19:41:32 +02:00
if ( rate_limited ) {
alert ( 'Cannot check any domain availability as a rate limit occurred.' );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
if ( registered . length != 0 ) {
alert ( 'The domain(s) ' + registered . join ( ', ' ) + ' is/are already registered.' );
} else {
2023-03-29 02:37:31 +02:00
alert ( 'All domains are available.' );
}
}
2023-04-01 19:41:32 +02:00
$ ( '.dns_checker' ). onclick = checkDomain ;
$ ( '#bytes_checker' ). onclick = async function () {
2023-03-29 02:37:31 +02:00
$ ( '#bytes_checker' ). innerHTML = 'Please wait...' ;
let inscribed_already = [];
let errors = [];
2023-04-01 19:41:32 +02:00
for ( let i = 0 ; i < files . length ; i ++ ) {
$ ( '#bytes_checker' ). innerHTML = 'Please wait...(' + ( i + 1 ) + '/' + files . length + ')' ;
2023-03-29 02:37:31 +02:00
let hash_result = await getData ( 'https://ordinalsbot.com/api/search?hash=' + files [ i ]. sha256 );
2023-04-01 19:41:32 +02:00
try {
2023-03-29 02:37:31 +02:00
hash_result = JSON . parse ( hash_result );
2023-04-01 19:41:32 +02:00
if ( hash_result . results . length != 0 ) {
2023-03-29 02:37:31 +02:00
inscribed_already . push ( files [ i ]. name );
2023-03-14 10:27:09 -05:00
}
2023-04-01 19:41:32 +02:00
} catch ( e ) {
2023-03-29 02:37:31 +02:00
errors . push ( files [ i ]. name );
2023-03-16 12:33:19 -05:00
}
2023-03-29 02:37:31 +02:00
await sleep ( 1000 );
}
2023-04-01 19:41:32 +02:00
if ( inscribed_already . length != 0 ) {
2023-03-29 02:37:31 +02:00
alert ( "The following files are inscribed already: " + inscribed_already . join ( ', ' ));
}
2023-04-01 19:41:32 +02:00
if ( errors . length != 0 ) {
2023-03-29 02:37:31 +02:00
alert ( "Could not check the following files due to an error: " + inscribed_already . join ( ', ' ));
}
2023-04-01 19:41:32 +02:00
if ( inscribed_already . length == 0 ) {
2023-03-29 02:37:31 +02:00
alert ( "Your files seem not to be inscribed yet." );
}
$ ( '#bytes_checker' ). innerHTML = 'Check if file(s) are inscribed already' ;
}
2023-04-01 19:41:32 +02:00
async function init ( num ) {
if ( ! num ) {
2023-03-29 02:37:31 +02:00
let isSafari = /^((?!chrome|android).)*safari/i . test ( navigator . userAgent );
2023-04-01 19:41:32 +02:00
if ( isSafari ) $ ( '.safari_warning' ). style . display = "block" ;
2023-03-29 02:37:31 +02:00
let minfee = await getMinFeeRate ();
2023-04-01 19:41:32 +02:00
$ ( '#sats_per_byte' ). innerText = minfee ;
$ ( '#sats_range' ). value = minfee ;
2023-03-29 02:37:31 +02:00
}
num = num + 1 ;
let allrates = await getAllFeeRates ();
2023-04-01 19:41:32 +02:00
$ ( '.minfee .num' ). innerText = allrates [ "minimumFee" ];
$ ( '.midfee .num' ). innerText = allrates [ "hourFee" ];
$ ( '.maxfee .num' ). innerText = allrates [ "fastestFee" ];
2023-03-29 02:37:31 +02:00
let isgreen ;
2023-04-01 19:41:32 +02:00
$$ ( '.fee .num' ). forEach ( function ( item ) {
if ( item . style . backgroundColor == "green" || getComputedStyle ( item ). backgroundColor == "rgb(0, 128, 0)" ) isgreen = item ;
2023-03-29 02:37:31 +02:00
});
2023-04-01 19:41:32 +02:00
if ( isgreen ) {
$ ( '#sats_per_byte' ). innerText = Number ( isgreen . innerText );
$ ( '#sats_range' ). value = Number ( isgreen . innerText );
sessionStorage [ "feerate" ] = isgreen . innerText ;
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
sessionStorage [ "bitcoin_price" ] = await getBitcoinPrice ();
await waitSomeSeconds ( 10 );
init ( num );
2023-03-29 02:37:31 +02:00
}
2023-04-01 19:41:32 +02:00
init ( 0 );
2023-03-29 02:37:31 +02:00
</ script >
< div class = "black-bg" onclick = "modalVanish();" ></ div >
2023-03-29 15:56:40 +02:00
< div class = "modal" >
< span id = "descendants-warning" style = "display:none;color:red;margin-bottom: 20px;font-size: 13px;" > For the remaining inscriptions, please wait for the parent transaction to confirm. Do NOT close this window.</ span >
< div class = "modal-content text-truncate" ></ div >
2023-04-01 19:41:32 +02:00
< button id = "modal-reset" class = "btn btn-primary" style = "margin-top: 15px; display: none;"
onclick = "location.reload();" > Reset
</ button >
2023-03-29 15:56:40 +02:00
</ div >
2023-03-29 02:37:31 +02:00
</ body >
2023-03-14 10:27:09 -05:00
</ html >