Simple block halving countdown website
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

main.js 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * SPDX-License-Identifier: AGPL-3.0-or-later
  3. */
  4. const API_PREFIX = "https://blockstream.info/api";
  5. const IRON_BLOCK = "https://gamepedia.cursecdn.com/minecraft_gamepedia/7/7e/Block_of_Iron_JE4_BE3.png?version=692673bafa1e94785ab6012d7a3c8dc4";
  6. const GOLD_BLOCK = "https://gamepedia.cursecdn.com/minecraft_gamepedia/7/72/Block_of_Gold_JE6_BE3.png?version=9d1a63c717df80feffa9ec93ebceb014";
  7. var nextHalvingHeight = 630000;
  8. var curTipBlock = null;
  9. var recentBlocks = null;
  10. function doApiReq(endpoint, cb) {
  11. let xhr = new XMLHttpRequest();
  12. xhr.timeout = 5000;
  13. xhr.onreadystatechange = function() {
  14. if (xhr.readyState == 4) {
  15. if (xhr.status == 200) {
  16. let json = JSON.parse(xhr.responseText);
  17. cb(json);
  18. } else {
  19. // TODO Make this better?
  20. cb(null);
  21. }
  22. }
  23. };
  24. xhr.open("GET", API_PREFIX + endpoint);
  25. xhr.send()
  26. }
  27. function doGetRecentBlocks(cb) {
  28. doApiReq("/blocks/tip", cb);
  29. }
  30. function doGetBlockCoinBaseTx(blockhash, cb) {
  31. doApiReq("/block/" + blockhash + "/txs", function(resp) {
  32. if (resp != null) {
  33. cb(resp[0]);
  34. } else {
  35. cb(null);
  36. }
  37. });
  38. }
  39. function calcRenderedSatQty(sats, decimals) {
  40. let subBtc = sats % 100000000;
  41. let btc = (sats - subBtc) / 100000000;
  42. let maskModulus = 10 ** (8 - decimals);
  43. let subDecimalRem = subBtc % maskModulus;
  44. let decimal = (subBtc - subDecimalRem) / maskModulus;
  45. if (decimal != 0) {
  46. return btc.toString() + "." + decimal.toString();
  47. } else {
  48. return btc.toString();
  49. }
  50. }
  51. const HALVING_PERIOD = 210000;
  52. function calcRewardAtHeight(height) {
  53. let epoch = Math.floor(height / HALVING_PERIOD);
  54. return 5000000000 / (2 ** epoch);
  55. }
  56. function calcRewardBreakdown(height, sats) {
  57. let subsidy = calcRewardAtHeight(height);
  58. let fees = sats - subsidy;
  59. let rSubsidy = calcRenderedSatQty(subsidy, 2);
  60. let rFees = calcRenderedSatQty(fees, 3);
  61. return rSubsidy + " subsidy + " + rFees + " fees"
  62. }
  63. function toggleElemSelected(elem) {
  64. if (elem.classList.contains("blbselected")) {
  65. elem.classList.remove("blbselected");
  66. } else {
  67. elem.classList.add("blbselected");
  68. }
  69. }
  70. function makeBlockElem(block, prevBlock) {
  71. let entry = document.createElement("div");
  72. let innerElem = document.createElement("div");
  73. innerElem.classList.add("entryinner");
  74. entry.appendChild(innerElem);
  75. /* ===== Top row data ===== */
  76. let topElem = document.createElement("div");
  77. topElem.classList.add("entrytop");
  78. innerElem.appendChild(topElem);
  79. // Make the icon.
  80. let iconElem = document.createElement("img");
  81. iconElem.classList.add("blbicon");
  82. topElem.appendChild(iconElem);
  83. if (block.height % HALVING_PERIOD == 0) {
  84. iconElem.src = GOLD_BLOCK;
  85. } else {
  86. iconElem.src = IRON_BLOCK;
  87. }
  88. // Element for all the data.
  89. let dataElem = document.createElement("div");
  90. dataElem.classList.add("blbdata");
  91. topElem.appendChild(dataElem);
  92. // Make the height element.
  93. let heightElem = document.createElement("span");
  94. heightElem.classList.add("blbheight");
  95. heightElem.innerHTML = block.height.toString();
  96. dataElem.appendChild(heightElem);
  97. // Make the reward breakdown element.
  98. let rewardElem = document.createElement("span");
  99. rewardElem.innerHTML = "Reward: ? sat";
  100. dataElem.appendChild(rewardElem);
  101. doGetBlockCoinBaseTx(block.id, function(resp) {
  102. let reward = resp.vout[0].value;
  103. let rewardStr = calcRewardBreakdown(block.height, reward);
  104. rewardElem.innerHTML = rewardStr;
  105. });
  106. // Make the hash element.
  107. let hashElem = document.createElement("span");
  108. hashElem.innerHTML = block.id;
  109. hashElem.classList.add("blbhash");
  110. topElem.appendChild(hashElem);
  111. /* ==== Detail row data ===== */
  112. // Details
  113. let detailElem = document.createElement("div");
  114. detailElem.classList.add("entrydetail");
  115. // Weight and height
  116. let sizeElem = document.createElement("div");
  117. detailElem.appendChild(sizeElem);
  118. let blockKB = Math.floor(block.size / 1024);
  119. let blockkWU = Math.floor(block.weight / 1024);
  120. detailElem.innerHTML = "Weight/Size: " + blockKB + " KiB / " + blockkWU + " kSipa";
  121. // Number of transactions
  122. let txsElem = document.createElement("div");
  123. detailElem.appendChild(txsElem);
  124. txsElem.innerHTML = "Tx count: " + block.tx_count;
  125. // Make the duration element.
  126. if (prevBlock != null) {
  127. let timeElem = document.createElement("div");
  128. let blocktime = block.timestamp - prevBlock.timestamp;
  129. if (blocktime < 0) {
  130. blocktime = 0; // ehhhhhhhh
  131. }
  132. timeElem.innerHTML = "Since last block: " + blocktime.toString() + " sec";
  133. detailElem.appendChild(timeElem);
  134. } else {
  135. let oopsElem = document.createElement("div");
  136. oopsElem.innerHTML = "<br/>(I don't feel like making the request to get the previous block to find the blocktime)";
  137. detailElem.appendChild(oopsElem);
  138. }
  139. /* ===== Finalize ===== */
  140. // Put it together.
  141. innerElem.appendChild(detailElem);
  142. entry.appendChild(innerElem);
  143. entry.classList.add("blocklistentry");
  144. entry.onclick = function() {
  145. toggleElemSelected(entry);
  146. };
  147. return entry;
  148. }
  149. var blocksleftElem = null;
  150. function updateCurrentTip(block) {
  151. if (blocksleftElem == null) {
  152. blocksleftElem = document.getElementById("blocksleft");
  153. }
  154. blocksleftElem.innerHTML = (nextHalvingHeight - curTipBlock.height).toString()
  155. }
  156. function populateRecentBlocks(blocks) {
  157. let blocklist = document.getElementById("blocklist");
  158. let loadingElem = document.getElementById("blocklistloading");
  159. curTipBlock = blocks[0];
  160. updateCurrentTip(curTipBlock.height);
  161. for (let i = 0; i < blocks.length; i++) {
  162. if (i < blocks.length - 1) {
  163. let elem = makeBlockElem(blocks[i], blocks[i + 1]);
  164. blocklist.appendChild(elem);
  165. } else {
  166. let elem = makeBlockElem(blocks[i], null);
  167. blocklist.appendChild(elem);
  168. }
  169. }
  170. loadingElem.style.display = "none";
  171. }
  172. function init() {
  173. console.log("Hello!");
  174. let checkHeights = [0, 1, 2, 209999, 210000, 210001, 629999, 630000, 630001];
  175. for (let i = 0; i < checkHeights.length; i++) {
  176. console.log("Reward at " + checkHeights[i] + " is " + calcRewardAtHeight(checkHeights[i]));
  177. }
  178. doGetRecentBlocks(function(blocks) {
  179. if (blocks == null) {
  180. alert("Error loading blocks, please refresh the page.");
  181. return;
  182. }
  183. recentBlocks = blocks;
  184. populateRecentBlocks(blocks);
  185. // TODO Set up more stuff.
  186. });
  187. }