Simple block halving countdown website
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

main.js 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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 formatDate(date) {
  71. let year = date.getUTCFullYear();
  72. let month = "0" + (1 + date.getUTCMonth());
  73. let day = "0" + (1 + date.getUTCDate());
  74. let hours = "0" + date.getUTCHours();
  75. let minutes = "0" + date.getUTCMinutes();
  76. let seconds = "0" + date.getUTCSeconds();
  77. let datePart = year + "-" + month.substr(-2) + "-" + day.substr(-2);
  78. let timePart = hours.substr(-2) + ":" + minutes.substr(-2) + ":" + seconds.substr(-2);
  79. return datePart + " " + timePart;
  80. }
  81. function makeBlockElem(block, prevBlock) {
  82. let entry = document.createElement("div");
  83. let innerElem = document.createElement("div");
  84. innerElem.classList.add("entryinner");
  85. entry.appendChild(innerElem);
  86. /* ===== Top row data ===== */
  87. let topElem = document.createElement("div");
  88. topElem.classList.add("entrytop");
  89. innerElem.appendChild(topElem);
  90. // Make the icon.
  91. let iconCtr = document.createElement("div");
  92. iconCtr.classList.add("blbiconctr");
  93. topElem.appendChild(iconCtr);
  94. let iconElem = document.createElement("img");
  95. iconElem.classList.add("blbicon");
  96. iconCtr.appendChild(iconElem);
  97. if (block.height % HALVING_PERIOD == 0) {
  98. iconElem.src = GOLD_BLOCK;
  99. } else {
  100. iconElem.src = IRON_BLOCK;
  101. }
  102. // Element for all the data.
  103. let dataElem = document.createElement("div");
  104. dataElem.classList.add("blbdata");
  105. topElem.appendChild(dataElem);
  106. // Make the height element.
  107. let heightElem = document.createElement("span");
  108. heightElem.classList.add("blbheight");
  109. heightElem.innerHTML = block.height.toString();
  110. dataElem.appendChild(heightElem);
  111. // this makes it look a lot nicer
  112. dataElem.append("—");
  113. // Make the timestamp element.
  114. let timestampElem = document.createElement("span");
  115. let timestampDate = new Date(block.timestamp * 1000);
  116. timestampElem.innerHTML = formatDate(timestampDate);
  117. dataElem.appendChild(timestampElem);
  118. // this makes it look a lot nicer
  119. dataElem.append("—");
  120. // Make the reward breakdown element.
  121. let rewardElem = document.createElement("span");
  122. rewardElem.innerHTML = "Reward: ? sat";
  123. dataElem.appendChild(rewardElem);
  124. doGetBlockCoinBaseTx(block.id, function(resp) {
  125. let reward = resp.vout[0].value;
  126. let rewardStr = calcRewardBreakdown(block.height, reward);
  127. rewardElem.innerHTML = rewardStr;
  128. });
  129. // Make the hash element.
  130. let hashElem = document.createElement("div");
  131. hashElem.innerHTML = block.id;
  132. hashElem.classList.add("blbhash");
  133. topElem.appendChild(hashElem);
  134. /* ==== Detail row data ===== */
  135. // Details
  136. let detailElem = document.createElement("div");
  137. detailElem.classList.add("entrydetail");
  138. // Weight and height
  139. let sizeElem = document.createElement("div");
  140. detailElem.appendChild(sizeElem);
  141. let blockKB = Math.floor(block.size / 1024);
  142. let blockkWU = Math.floor(block.weight / 1024);
  143. sizeElem.innerHTML = "Weight/Size: " + blockKB + " KiB / " + blockkWU + " kSipa";
  144. // Number of transactions
  145. let txsElem = document.createElement("div");
  146. detailElem.appendChild(txsElem);
  147. txsElem.innerHTML = "Tx count: " + block.tx_count;
  148. // Make the duration element.
  149. if (prevBlock != null) {
  150. let timeElem = document.createElement("div");
  151. let blocktime = block.timestamp - prevBlock.timestamp;
  152. if (blocktime < 0) {
  153. blocktime = 0; // ehhhhhhhh
  154. }
  155. timeElem.innerHTML = "Since last block: " + blocktime.toString() + " sec";
  156. detailElem.appendChild(timeElem);
  157. } else {
  158. let oopsElem = document.createElement("div");
  159. oopsElem.innerHTML = "<br/>(I don't feel like making the request to get the previous block to find the blocktime)";
  160. detailElem.appendChild(oopsElem);
  161. }
  162. let linksElem = document.createElement("div");
  163. linksElem.classList.add("detaillinks");
  164. detailElem.appendChild(linksElem);
  165. let blockstreamLink = document.createElement("a");
  166. blockstreamLink.href = "https://blockstream.info/block/"+ block.id;
  167. let blockstreamLogo = document.createElement("img");
  168. blockstreamLogo.src = "https://blockstream.info/img/icons/blockstream-logo.png";
  169. blockstreamLink.appendChild(blockstreamLogo);
  170. let viewonElem = document.createElement("span");
  171. viewonElem.innerHTML = "View on blockstream.info";
  172. blockstreamLink.appendChild(viewonElem);
  173. linksElem.appendChild(blockstreamLink);
  174. /* ===== Finalize ===== */
  175. // Put it together.
  176. innerElem.appendChild(detailElem);
  177. entry.appendChild(innerElem);
  178. entry.classList.add("blocklistentry");
  179. entry.onclick = function() {
  180. toggleElemSelected(entry);
  181. };
  182. return entry;
  183. }
  184. var blocksleftElem = null;
  185. function updateCurrentTip(block) {
  186. if (blocksleftElem == null) {
  187. blocksleftElem = document.getElementById("blocksleft");
  188. }
  189. blocksleftElem.innerHTML = (nextHalvingHeight - curTipBlock.height).toString()
  190. }
  191. function populateRecentBlocks(blocks) {
  192. let blocklist = document.getElementById("blocklist");
  193. let loadingElem = document.getElementById("blocklistloading");
  194. curTipBlock = blocks[0];
  195. updateCurrentTip(curTipBlock.height);
  196. for (let i = 0; i < blocks.length; i++) {
  197. if (i < blocks.length - 1) {
  198. let elem = makeBlockElem(blocks[i], blocks[i + 1]);
  199. blocklist.appendChild(elem);
  200. } else {
  201. let elem = makeBlockElem(blocks[i], null);
  202. blocklist.appendChild(elem);
  203. }
  204. }
  205. loadingElem.style.display = "none";
  206. }
  207. function init() {
  208. console.log("Hello!");
  209. let checkHeights = [0, 1, 2, 209999, 210000, 210001, 629999, 630000, 630001];
  210. for (let i = 0; i < checkHeights.length; i++) {
  211. console.log("Reward at " + checkHeights[i] + " is " + calcRewardAtHeight(checkHeights[i]));
  212. }
  213. doGetRecentBlocks(function(blocks) {
  214. if (blocks == null) {
  215. alert("Error loading blocks, please refresh the page.");
  216. return;
  217. }
  218. recentBlocks = blocks;
  219. populateRecentBlocks(blocks);
  220. // TODO Set up more stuff.
  221. });
  222. }