-
-
-
-
+const BlocksPage = () => {
+ return (
+
+
+
+
-}
\ No newline at end of file
+ );
+};
+
+export default BlocksPage;
diff --git a/src/components/BlueScoreContext.js b/src/components/BlueScoreContext.js
index 3113762..1f8917a 100644
--- a/src/components/BlueScoreContext.js
+++ b/src/components/BlueScoreContext.js
@@ -1,7 +1,6 @@
-import {createContext} from "react";
+import { createContext } from "react";
-
-const BlueScoreContext = createContext({'blueScore': 0});
+const BlueScoreContext = createContext({ blueScore: 0 });
BlueScoreContext.displayName = "VirtualSelectedParentBlueScore";
export default BlueScoreContext;
diff --git a/src/components/CoinsupplyBox.js b/src/components/CoinsupplyBox.js
index 6de7999..6935296 100644
--- a/src/components/CoinsupplyBox.js
+++ b/src/components/CoinsupplyBox.js
@@ -1,143 +1,174 @@
-import {faCoins} from '@fortawesome/free-solid-svg-icons';
-import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
-import moment from 'moment';
-import {useEffect, useState} from "react";
-import {numberWithCommas} from "../helper";
-import {getCoinSupply, getHalving} from '../kaspa-api-client';
-import {API_SERVER, KASPA_UNIT} from "../explorer_constants";
-
+import { faCoins } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import moment from "moment";
+import { useEffect, useState, useCallback } from "react";
+import { numberWithCommas } from "../helper";
+import { getCoinSupply, getHalving } from "../kaspa-api-client";
+import { API_SERVER, KASPA_UNIT } from "../explorer_constants";
const CBox = () => {
- const [circCoins, setCircCoins] = useState("-");
- const [blockReward, setBlockReward] = useState("-");
- const [halvingDate, setHalvingDate] = useState("-");
- const [halvingAmount, setHalvingAmount] = useState("-");
-
- const initBox = async () => {
- if (localStorage.getItem("cacheCircCoins")) {
- setCircCoins(localStorage.getItem("cacheCircCoins"))
- }
-
- if (localStorage.getItem("cacheBlockReward")) {
- setBlockReward(localStorage.getItem("cacheBlockReward"))
- }
-
- if (localStorage.getItem("cacheHalvingDate")) {
- setHalvingDate(localStorage.getItem("cacheHalvingDate"))
- }
-
- if (localStorage.getItem("cacheHalvingAmount")) {
- setHalvingAmount(localStorage.getItem("cacheHalvingAmount"))
- }
-
-
- const coinSupplyResp = await getCoinSupply()
- getBlockReward();
-
- getHalving().then((d) => {
- setHalvingDate(moment(d.nextHalvingTimestamp * 1000).format("YYYY-MM-DD HH:mm"))
- setHalvingAmount(d.nextHalvingAmount.toFixed(2))
- localStorage.setItem("cacheHalvingDate", moment(d.nextHalvingTimestamp * 1000).format("YYYY-MM-DD HH:mm"))
- localStorage.setItem("cacheHalvingAmount", d.nextHalvingAmount.toFixed(2))
- })
-
- setCircCoins(Math.round(coinSupplyResp.circulatingSupply / 100000000))
- localStorage.setItem("cacheCircCoins", Math.round(coinSupplyResp.circulatingSupply / 100000000))
+ const [circCoins, setCircCoins] = useState("-");
+ const [blockReward, setBlockReward] = useState("-");
+ const [halvingDate, setHalvingDate] = useState("-");
+ const [halvingAmount, setHalvingAmount] = useState("-");
+
+ const initBox = useCallback(async () => {
+ if (localStorage.getItem("cacheCircCoins")) {
+ setCircCoins(localStorage.getItem("cacheCircCoins"));
}
- useEffect(() => {
- initBox();
-
- const updateCircCoins = setInterval(async () => {
-
- const coinSupplyResp = await getCoinSupply()
- setCircCoins(Math.round(coinSupplyResp.circulatingSupply / 100000000))
-
- }, 10000)
-
-
- return async () => {
- clearInterval(updateCircCoins)
- };
- }, [])
-
- async function getBlockReward() {
- await fetch(`${API_SERVER}/info/blockreward`)
- .then((response) => response.json())
- .then(d => {
- setBlockReward(d.blockreward.toFixed(2))
- localStorage.setItem("cacheBlockReward", d.blockreward.toFixed(2))
-
- })
- .catch(err => console.log("Error", err))
+ if (localStorage.getItem("cacheBlockReward")) {
+ setBlockReward(localStorage.getItem("cacheBlockReward"));
}
+ if (localStorage.getItem("cacheHalvingDate")) {
+ setHalvingDate(localStorage.getItem("cacheHalvingDate"));
+ }
- useEffect(() => {
- document.getElementById('coins').animate([
- // keyframes
- {opacity: '1'},
- {opacity: '0.6'},
- {opacity: '1'},
- ], {
- // timing options
- duration: 300
- });
- }, [circCoins])
-
+ if (localStorage.getItem("cacheHalvingAmount")) {
+ setHalvingAmount(localStorage.getItem("cacheHalvingAmount"));
+ }
- return <>
-
-
-
-
-
-
- |
-
-
-
- Coin supply
- |
-
-
-
- Total
- |
-
- {numberWithCommas(circCoins)} {KASPA_UNIT}
-
- |
-
-
- Max (approx.) |
- 28,700,000,000 {KASPA_UNIT} |
-
-
- Mined |
- {(circCoins / 28700000000 * 100).toFixed(2)} % |
-
-
- Block reward |
- {process.env.REACT_APP_NETWORK === "mainnet" ? `${blockReward} ${KASPA_UNIT}` : '-'} |
-
-
- Reward reduction
- {/* Here is some information about the chromatic halving..}>
+ const coinSupplyResp = await getCoinSupply();
+ getBlockReward();
+
+ getHalving().then((d) => {
+ setHalvingDate(
+ moment(d.nextHalvingTimestamp * 1000).format("YYYY-MM-DD HH:mm"),
+ );
+ setHalvingAmount(d.nextHalvingAmount.toFixed(2));
+ localStorage.setItem(
+ "cacheHalvingDate",
+ moment(d.nextHalvingTimestamp * 1000).format("YYYY-MM-DD HH:mm"),
+ );
+ localStorage.setItem(
+ "cacheHalvingAmount",
+ d.nextHalvingAmount.toFixed(2),
+ );
+ });
+
+ setCircCoins(Math.round(coinSupplyResp.circulatingSupply / 100000000));
+ localStorage.setItem(
+ "cacheCircCoins",
+ Math.round(coinSupplyResp.circulatingSupply / 100000000),
+ );
+ }, []); // ensures `initBox` is memoized and does not trigger unnecessary re-renders
+
+ useEffect(() => {
+ initBox();
+
+ const updateCircCoins = setInterval(async () => {
+ const coinSupplyResp = await getCoinSupply();
+ setCircCoins(Math.round(coinSupplyResp.circulatingSupply / 100000000));
+ }, 10000);
+
+ return async () => {
+ clearInterval(updateCircCoins);
+ };
+ }, [initBox]);
+
+ async function getBlockReward() {
+ await fetch(`${API_SERVER}/info/blockreward`)
+ .then((response) => response.json())
+ .then((d) => {
+ setBlockReward(d.blockreward.toFixed(2));
+ localStorage.setItem("cacheBlockReward", d.blockreward.toFixed(2));
+ })
+ .catch((err) => console.log("Error", err));
+ }
+
+ useEffect(() => {
+ document.getElementById("coins").animate(
+ [
+ // keyframes
+ { opacity: "1" },
+ { opacity: "0.6" },
+ { opacity: "1" },
+ ],
+ {
+ // timing options
+ duration: 300,
+ },
+ );
+ }, [circCoins]);
+
+ return (
+ <>
+
+
+
+
+
+
+ |
+
+
+
+ Coin supply
+ |
+
+
+ Total |
+
+
+ {numberWithCommas(circCoins)} {KASPA_UNIT}
+
+ |
+
+
+
+ Max (approx.)
+ |
+ 28,700,000,000 {KASPA_UNIT} |
+
+
+ Mined |
+
+ {((circCoins / 28700000000) * 100).toFixed(2)} %
+ |
+
+
+ Block reward |
+
+ {process.env.REACT_APP_NETWORK === "mainnet"
+ ? `${blockReward} ${KASPA_UNIT}`
+ : "-"}
+ |
+
+
+
+ Reward reduction
+ {/* Here is some information about the chromatic halving..}>
*/}
- |
- {process.env.REACT_APP_NETWORK === "mainnet" ? <>{halvingDate}
- to {halvingAmount} {KASPA_UNIT}
- > : "-"}
- |
-
-
-
+ |
+
+ {process.env.REACT_APP_NETWORK === "mainnet" ? (
+ <>
+ {halvingDate}
+
+
+ to {halvingAmount} {KASPA_UNIT}
+
+ >
+ ) : (
+ "-"
+ )}
+ |
+
+
+
>
-}
-
+ );
+};
-export default CBox
+export default CBox;
diff --git a/src/components/CopyButton.js b/src/components/CopyButton.js
index e466901..de9530a 100644
--- a/src/components/CopyButton.js
+++ b/src/components/CopyButton.js
@@ -1,36 +1,53 @@
-import {useState} from "react"
-import {OverlayTrigger, Tooltip} from "react-bootstrap"
-import {BiCopy} from "react-icons/bi"
-import {FaCheck} from "react-icons/fa"
+import { useState } from "react";
+import { OverlayTrigger, Tooltip } from "react-bootstrap";
+import { BiCopy } from "react-icons/bi";
+import { FaCheck } from "react-icons/fa";
-export default (props) => {
+// assign component to a variable before exporting
+const CopyToClipboard = (props) => {
+ const [justCopied, setJustCopied] = useState(false);
- const [justCopied, setJustCopied] = useState(false)
+ // useEffect(() => {
+ // document.getElementById('coins').animate([
+ // // keyframes
+ // { transform: 'rotate(0.5turn)'}
+ // ], {
+ // // timing options
+ // duration: 300
+ // });
+ // }, [justCopied])
- // useEffect(() => {
- // document.getElementById('coins').animate([
- // // keyframes
- // { transform: 'rotate(0.5turn)'}
- // ], {
- // // timing options
- // duration: 300
- // });
- // }, [justCopied])
+ const handleOnClick = (e) => {
+ setJustCopied(true);
+ navigator.clipboard.writeText(props.text);
+ setTimeout(() => {
+ setJustCopied(false);
+ }, 1000);
+ };
- const handleOnClick = (e) => {
- setJustCopied(true)
- navigator.clipboard.writeText(props.text)
- setTimeout(() => {
- setJustCopied(false)
- }, 1000)
- }
+ if (justCopied) {
+ return (
+
Copied}
+ >
+
+
+
+
+ );
+ } else {
+ return (
+
Copy to clipboard}
+ >
+
+
+
+
+ );
+ }
+};
- if (justCopied) {
- return
Copied}>
- } else {
- return
Copy to clipboard}>
- }
-}
\ No newline at end of file
+export default CopyToClipboard;
diff --git a/src/components/LastBlocksContext.js b/src/components/LastBlocksContext.js
index eff3736..0903926 100644
--- a/src/components/LastBlocksContext.js
+++ b/src/components/LastBlocksContext.js
@@ -1,5 +1,4 @@
-import {createContext} from "react";
-
+import { createContext } from "react";
const LastBlocksContext = createContext({});
LastBlocksContext.displayName = "LastBlocks";
diff --git a/src/components/MarketDataBox.js b/src/components/MarketDataBox.js
index d9d562a..73960de 100644
--- a/src/components/MarketDataBox.js
+++ b/src/components/MarketDataBox.js
@@ -1,82 +1,129 @@
-import {useContext, useEffect, useState} from "react";
-import {HiCurrencyDollar} from 'react-icons/hi';
-import {IoMdTrendingDown, IoMdTrendingUp} from 'react-icons/io';
+import { useContext, useEffect, useState } from "react";
+import { HiCurrencyDollar } from "react-icons/hi";
+import { IoMdTrendingDown, IoMdTrendingUp } from "react-icons/io";
import { KASPA_UNIT } from "../explorer_constants";
-import {numberWithCommas} from "../helper";
-import {getCoinSupply} from '../kaspa-api-client';
+import { numberWithCommas } from "../helper";
+import { getCoinSupply } from "../kaspa-api-client";
import PriceContext from "./PriceContext";
-
const MarketDataBox = () => {
- const [circCoinsMData, setCircCoinsMData] = useState("-");
- const {price, marketData} = useContext(PriceContext);
-
- const initBox = async () => {
- const coin_supply = await getCoinSupply()
- setCircCoinsMData(Math.round(parseFloat(coin_supply.circulatingSupply) / 100000000));
- }
+ const [circCoinsMData, setCircCoinsMData] = useState("-");
+ const { price, marketData } = useContext(PriceContext);
- useEffect(() => {
- initBox();
- }, [])
+ const initBox = async () => {
+ const coin_supply = await getCoinSupply();
+ setCircCoinsMData(
+ Math.round(parseFloat(coin_supply.circulatingSupply) / 100000000),
+ );
+ };
+ useEffect(() => {
+ initBox();
+ }, []);
- return <>
-
-
-
-
-
-
- |
-
-
-
- Market data
- |
-
-
- Price |
- $ {price} / {KASPA_UNIT} |
-
-
- 1h % |
-
- {marketData?.price_change_percentage_1h_in_currency?.usd > 0 ?
- : }
- {marketData?.price_change_percentage_1h_in_currency?.usd?.toFixed(1)} %
- |
-
-
- 24h % |
-
- {marketData?.price_change_percentage_24h > 0 ? :
- }
- {marketData?.price_change_percentage_24h?.toFixed(1)} %
- |
-
-
- 7d % |
-
- {marketData?.price_change_percentage_7d > 0 ? :
- }
- {marketData?.price_change_percentage_7d?.toFixed(1)} %
- |
-
-
- Volume |
- $ {numberWithCommas(marketData?.total_volume?.usd)} |
-
-
- MCAP |
- $ {(circCoinsMData * price / 1000000000).toFixed(2)} B Rank
- #{marketData?.market_cap_rank} |
-
-
-
+ return (
+ <>
+
+
+
+
+
+
+ |
+
+
+
+ Market data
+ |
+
+
+ Price |
+
+ $ {price} / {KASPA_UNIT}
+ |
+
+
+
+ 1h %
+ |
+
+ {marketData?.price_change_percentage_1h_in_currency?.usd > 0 ? (
+
+ ) : (
+
+ )}
+ {marketData?.price_change_percentage_1h_in_currency?.usd?.toFixed(
+ 1,
+ )}{" "}
+ %
+ |
+
+
+
+ 24h %
+ |
+
+ {marketData?.price_change_percentage_24h > 0 ? (
+
+ ) : (
+
+ )}
+ {marketData?.price_change_percentage_24h?.toFixed(1)} %
+ |
+
+
+
+ 7d %
+ |
+
+ {marketData?.price_change_percentage_7d > 0 ? (
+
+ ) : (
+
+ )}
+ {marketData?.price_change_percentage_7d?.toFixed(1)} %
+ |
+
+
+ Volume |
+
+ $ {numberWithCommas(marketData?.total_volume?.usd)}
+ |
+
+
+ MCAP |
+
+ $ {((circCoinsMData * price) / 1000000000).toFixed(2)} B{" "}
+
+ Rank #{marketData?.market_cap_rank}
+
+ |
+
+
+
>
-}
-
+ );
+};
-export default MarketDataBox
+export default MarketDataBox;
diff --git a/src/components/NotFound.js b/src/components/NotFound.js
index 00e520e..17205be 100644
--- a/src/components/NotFound.js
+++ b/src/components/NotFound.js
@@ -1,7 +1,9 @@
const NotFound = () => {
- return
-
Page not found
+ return (
+
+
Page not found
-}
+ );
+};
-export default NotFound;
\ No newline at end of file
+export default NotFound;
diff --git a/src/components/PriceContext.js b/src/components/PriceContext.js
index 91334c9..9ee23d6 100644
--- a/src/components/PriceContext.js
+++ b/src/components/PriceContext.js
@@ -1,7 +1,6 @@
-import {createContext} from "react";
+import { createContext } from "react";
-
-const PriceContext = createContext({'price': 0});
+const PriceContext = createContext({ price: 0 });
PriceContext.displayName = "KasPrice";
export default PriceContext;
diff --git a/src/components/QrButton.js b/src/components/QrButton.js
index 9bea591..97ead00 100644
--- a/src/components/QrButton.js
+++ b/src/components/QrButton.js
@@ -1,9 +1,21 @@
-import {OverlayTrigger, Tooltip} from "react-bootstrap"
-import {FaQrcode} from "react-icons/fa"
+import { OverlayTrigger, Tooltip } from "react-bootstrap";
+import { FaQrcode } from "react-icons/fa";
-export default (props) => {
+const QrButton = (props) => {
+ return (
+
Show QR-Code}
+ >
+
+
+
+
+ );
+};
-
- return
Show QR-Code}>
-}
\ No newline at end of file
+export default QrButton;
diff --git a/src/components/TransactionInfo.js b/src/components/TransactionInfo.js
index cb4321d..f4a29d3 100644
--- a/src/components/TransactionInfo.js
+++ b/src/components/TransactionInfo.js
@@ -1,183 +1,273 @@
-/* global BigInt */
-
import moment from "moment";
-import {useContext, useEffect, useRef, useState} from 'react';
-import {Col, Container, Row, Spinner} from "react-bootstrap";
-import {useParams} from "react-router";
-import {Link} from "react-router-dom";
-import {KASPA_UNIT} from "../explorer_constants.js";
-import {numberWithCommas} from "../helper.js";
-import {getTransaction, getTransactions} from '../kaspa-api-client.js';
+import { useCallback, useContext, useEffect, useRef, useState } from "react";
+import { Col, Container, Row, Spinner } from "react-bootstrap";
+import { useParams } from "react-router";
+import { Link } from "react-router-dom";
+import { KASPA_UNIT } from "../explorer_constants.js";
+import { numberWithCommas } from "../helper.js";
+import { getTransaction, getTransactions } from "../kaspa-api-client.js";
import BlueScoreContext from "./BlueScoreContext.js";
import CopyButton from "./CopyButton.js";
-import PriceContext from "./PriceContext.js";
-import {parseSignatureScript} from "../inscriptions";
+import { parseSignatureScript } from "../inscriptions";
const getOutputFromIndex = (outputs, index) => {
- for (const output of outputs) {
- if (output.index == index) {
- return output
- }
+ for (const output of outputs) {
+ if (output.index === index) {
+ return output;
}
-}
+ }
+};
const TransactionInfo = () => {
- const {id} = useParams();
- const [txInfo, setTxInfo] = useState()
- const [storageMass, setStorageMass] = useState()
- const [additionalTxInfo, setAdditionalTxInfo] = useState()
- const [showTxFee, setShowTxFee] = useState(false);
- const [error, setError] = useState(false)
- const {price} = useContext(PriceContext);
-
- const retryCnt = useRef(0)
- const retryNotAccepted = useRef(6)
- const {blueScore} = useContext(BlueScoreContext);
+ const { id } = useParams();
+ const [txInfo, setTxInfo] = useState();
+ // const [storageMass, setStorageMass] = useState();
+ const [additionalTxInfo, setAdditionalTxInfo] = useState();
+ const [showTxFee, setShowTxFee] = useState(false);
+ const setError = useState(false);
- const [blockColor, setBlockColor] = useState()
+ const retryCnt = useRef(0);
+ const retryNotAccepted = useRef(6);
+ const { blueScore } = useContext(BlueScoreContext);
- const getTx = () => getTransaction(id).then(
- (res) => {
- setTxInfo(res)
- })
- .catch((err) => {
- setError(true);
- setTxInfo(undefined);
- throw err
- })
+ const getTx = useCallback(() => {
+ getTransaction(id)
+ .then((res) => {
+ setTxInfo(res);
+ })
+ .catch((err) => {
+ setError(true);
+ setTxInfo(undefined);
+ throw err;
+ });
+ }, [id, setError]);
- useEffect(() => {
- setError(false);
- getTx()
- }, [id])
+ useEffect(() => {
+ setError(false);
+ getTx();
+ }, [id, getTx, setError]);
+ // const getAmountFromOutputs = (outputs, i) => {
+ // for (const o of outputs) {
+ // if (o.index === i) {
+ // return o.amount / 100000000;
+ // }
+ // }
+ // };
- const getAmountFromOutputs = (outputs, i) => {
- for (const o of outputs) {
- if (o.index == i) {
- return o.amount / 100000000
- }
- }
+ useEffect(() => {
+ // request TX input addresses
+ if (!!txInfo && txInfo?.detail !== "Transaction not found") {
+ const txToQuery = txInfo.inputs
+ ?.flatMap((txInput) => txInput.previous_outpoint_hash)
+ .filter((x) => x);
+ if (!!txToQuery) {
+ getTransactions(txToQuery, true, true)
+ .then((resp) => {
+ setShowTxFee(txToQuery.length === resp.length);
+ const respAsObj = resp.reduce((obj, cur) => {
+ obj[cur["transaction_id"]] = cur;
+ return obj;
+ }, {});
+ setAdditionalTxInfo(respAsObj);
+ })
+ .catch((err) => console.log("Error ", err));
+ }
+ }
+ if (txInfo?.detail === "Transaction not found") {
+ retryCnt.current += 1;
+ if (retryCnt.current < 20) {
+ setTimeout(getTx, 1000);
+ }
}
- useEffect(() => {
- // request TX input addresses
- if (!!txInfo && txInfo?.detail !== "Transaction not found") {
- const txToQuery = txInfo.inputs?.flatMap(txInput => txInput.previous_outpoint_hash).filter(x => x)
- if (!!txToQuery) {
- getTransactions(txToQuery, true, true).then(
- resp => {
- setShowTxFee(txToQuery.length == resp.length)
- const respAsObj = resp.reduce((obj, cur) => {
- obj[cur["transaction_id"]] = cur
- return obj;
- }, {});
- setAdditionalTxInfo(respAsObj)
- }
- ).catch(err => console.log("Error ", err))
- }
- }
- if (txInfo?.detail == "Transaction not found") {
- retryCnt.current += 1
- if (retryCnt.current < 20) {
- setTimeout(getTx, 1000);
- }
- }
-
-
- const timeDiff = (Date.now() - (txInfo?.block_time || Date.now())) / 1000
-
- if (txInfo?.is_accepted === false && timeDiff < 60 && retryNotAccepted.current > 0) {
- retryNotAccepted.current -= 1
- setTimeout(getTx, 2000);
- }
-
-
- }, [txInfo])
+ const timeDiff = (Date.now() - (txInfo?.block_time || Date.now())) / 1000;
+ if (
+ txInfo?.is_accepted === false &&
+ timeDiff < 60 &&
+ retryNotAccepted.current > 0
+ ) {
+ retryNotAccepted.current -= 1;
+ setTimeout(getTx, 2000);
+ }
+ }, [txInfo, getTx]);
- return
-
-
-
- {!!txInfo && txInfo?.detail !== "Transaction not found" ?
-
-
transaction info
-
-
- Transaction Id
- {txInfo.transaction_id}
-
-
-
-
- Subnetwork Id
- {txInfo.subnetwork_id}
-
-
- Hash
- {txInfo.hash}
-
-
- Compute mass
- {txInfo.mass ? txInfo.mass : "-"}
-
- {/**/}
- {/* Storage mass*/}
- {/* {storageMass ? storageMass : "..."}*/}
- {/*
*/}
-
- Block Hashes
-
- {txInfo.block_hash?.map(x => -
- {x}
-
)}
-
-
-
- Block Time
- {moment(parseInt(txInfo.block_time)).format("YYYY-MM-DD HH:mm:ss")} ({txInfo.block_time})
-
-
- Accepting Block Hash
-
-
- {txInfo.accepting_block_hash || "-"}
-
-
-
- {showTxFee &&
- Transaction fee
-
- {txInfo && additionalTxInfo &&
- <>{(txInfo.inputs.map((tx_input) => (getOutputFromIndex(additionalTxInfo[tx_input.previous_outpoint_hash]?.outputs || [], tx_input.previous_outpoint_index)?.amount || 0)).reduce((a, b) => a + b) - (txInfo.outputs?.map((v) => v.amount) || [0]).reduce((a, b) => a + b)) / 100000000} {KASPA_UNIT}>
- }
-
-
}
-
- Details
-
- {txInfo.is_accepted ? accepted
:
- not accepted}
- {txInfo.is_accepted && blueScore !== 0 && (blueScore - txInfo.accepting_block_blue_score) < 86400 &&
- {Math.max(blueScore - txInfo.accepting_block_blue_score, 0)} confirmations
}
- {txInfo.is_accepted && blueScore !== 0 && (blueScore - txInfo.accepting_block_blue_score) >= 86400 &&
- confirmed
}
-
-
-
-
: <>Retry {retryCnt.current}/20
Sometimes TXs need a few seconds to be added into the
- database.
>}
-
-
+ return (
+
+
+
+
+ {!!txInfo && txInfo?.detail !== "Transaction not found" ? (
+
+
+
+ transaction info
+
+
+
+
+
+ Transaction Id
+
+
+ {txInfo.transaction_id}
+
+
+
+
+
+ Subnetwork Id
+
+
+ {txInfo.subnetwork_id}
+
+
+
+
+ Hash
+
+
+ {txInfo.hash}
+
+
+
+
+ Compute mass
+
+
+ {txInfo.mass ? txInfo.mass : "-"}
+
+
+ {/**/}
+ {/* Storage mass*/}
+ {/* {storageMass ? storageMass : "..."}*/}
+ {/*
*/}
+
+
+ Block Hashes
+
+
+
+ {txInfo.block_hash?.map((x) => (
+ -
+
+ {x}
+
+
+ ))}
+
+
+
+
+
+ Block Time
+
+
+ {moment(parseInt(txInfo.block_time)).format(
+ "YYYY-MM-DD HH:mm:ss",
+ )}{" "}
+ ({txInfo.block_time})
+
+
+
+
+ Accepting Block Hash
+
+
+
+ {txInfo.accepting_block_hash || "-"}
+
+
+
+ {showTxFee && (
+
+
+ Transaction fee
+
+
+ {txInfo && additionalTxInfo && (
+ <>
+ {(txInfo.inputs
+ .map(
+ (tx_input) =>
+ getOutputFromIndex(
+ additionalTxInfo[
+ tx_input.previous_outpoint_hash
+ ]?.outputs || [],
+ tx_input.previous_outpoint_index,
+ )?.amount || 0,
+ )
+ .reduce((a, b) => a + b) -
+ (
+ txInfo.outputs?.map((v) => v.amount) || [0]
+ ).reduce((a, b) => a + b)) /
+ 100000000}{" "}
+ {KASPA_UNIT}
+ >
+ )}
+
+
+ )}
+
+
+ Details
+
+
+ {txInfo.is_accepted ? (
+ accepted
+ ) : (
+
+ not accepted
+
+ )}
+ {txInfo.is_accepted &&
+ blueScore !== 0 &&
+ blueScore - txInfo.accepting_block_blue_score <
+ 86400 && (
+
+ {Math.max(
+ blueScore - txInfo.accepting_block_blue_score,
+ 0,
+ )}
+ confirmations
+
+ )}
+ {txInfo.is_accepted &&
+ blueScore !== 0 &&
+ blueScore - txInfo.accepting_block_blue_score >=
+ 86400 && (
+ confirmed
+ )}
+
+
+
+
+ ) : (
+ <>
+
+ Retry {retryCnt.current}/20
+
+ Sometimes TXs need a few seconds to be added into the
+ database.
+
+ >
+ )}
+
+
- {/* id = Column(Integer, primary_key=True)
+ {/* id = Column(Integer, primary_key=True)
transaction_id = Column(String)
index = Column(Integer)
@@ -187,124 +277,196 @@ const TransactionInfo = () => {
signatureScript = Column(String) # "41c903159094....281a1d26f70b0037d600554e01",
sigOpCount = Column(Integer) */}
-
-
- {!!txInfo && txInfo?.detail !== "Transaction not found" ?
-
-
Inputs
-
- {
- (txInfo.inputs || []).map((tx_input) => <>
-
-
- Signature Op Count
-
- {tx_input.sig_op_count}
-
-
-
- Signature Script
-
- {tx_input.signature_script}
-
- {tx_input.signature_script.includes('6b6173706c6578') && (
- <>
- KRC-20
-
- {parseSignatureScript(tx_input.signature_script).findLast(s => s.indexOf('OP_PUSH') === 0).split(' ')[1]}
-
- >
- )}
-
- {!!additionalTxInfo && additionalTxInfo[tx_input.previous_outpoint_hash] &&
-
- Amount
-
- -{numberWithCommas(getOutputFromIndex(additionalTxInfo[tx_input.previous_outpoint_hash]
- .outputs, tx_input.previous_outpoint_index).amount / 100000000)} {KASPA_UNIT}
-
- }
-
- Previous Outpoint Index + Hash
-
- #{tx_input.previous_outpoint_index} {tx_input.previous_outpoint_hash}
-
-
- {additionalTxInfo && additionalTxInfo[tx_input.previous_outpoint_hash] && <>
-
- Address
-
-
- {getOutputFromIndex(additionalTxInfo[tx_input.previous_outpoint_hash]
- .outputs, tx_input.previous_outpoint_index).script_public_key_address}
-
-
- >}
-
-
- >)
- }
- {(txInfo.inputs || []).length === 0 ?
-
- FROM
- COINBASE ( New coins )
-
-
: <>>}
-
-
Outputs
-
+
+
+ {!!txInfo && txInfo?.detail !== "Transaction not found" ? (
+
+
+
Inputs
+
+
+ {(txInfo.inputs || []).map((tx_input) => (
+ <>
+
+
+
+ Signature Op Count
+
+
+ {tx_input.sig_op_count}
+
+
+
+
+ Signature Script
+
+
+ {tx_input.signature_script}
+
+ {tx_input.signature_script.includes(
+ "6b6173706c6578",
+ ) && (
+ <>
+ KRC-20
+
{
- (txInfo.outputs || []).map((tx_output) =>
-
-
- Index
-
- #{tx_output.index}
-
-
-
- Script Public Key Type
-
- {tx_output.script_public_key_type}
-
-
-
- Amount
-
- +{numberWithCommas(tx_output.amount / 100000000)} {KASPA_UNIT}
-
-
-
- Script Public Key
-
- {tx_output.script_public_key}
-
-
-
- Script Public Key Address
-
-
-
- {tx_output.script_public_key_address}
-
-
-
-
)
+ parseSignatureScript(
+ tx_input.signature_script,
+ )
+ .findLast((s) => s.indexOf("OP_PUSH") === 0)
+ .split(" ")[1]
}
-
: <>>}
-
-
-
-
+
+ >
+ )}
+
+ {!!additionalTxInfo &&
+ additionalTxInfo[tx_input.previous_outpoint_hash] && (
+
+ Amount
+
+
+ -
+ {numberWithCommas(
+ getOutputFromIndex(
+ additionalTxInfo[
+ tx_input.previous_outpoint_hash
+ ].outputs,
+ tx_input.previous_outpoint_index,
+ ).amount / 100000000,
+ )}
+ {KASPA_UNIT}
+
+
+
+ )}
+
+
+ Previous Outpoint Index + Hash
+
+
+ #{tx_input.previous_outpoint_index}{" "}
+ {tx_input.previous_outpoint_hash}
+
+
+ {additionalTxInfo &&
+ additionalTxInfo[tx_input.previous_outpoint_hash] && (
+ <>
+
+
+ Address
+
+
+
+ {
+ getOutputFromIndex(
+ additionalTxInfo[
+ tx_input.previous_outpoint_hash
+ ].outputs,
+ tx_input.previous_outpoint_index,
+ ).script_public_key_address
+ }
+
+
+
+ >
+ )}
+
+ >
+ ))}
+ {(txInfo.inputs || []).length === 0 ? (
+
+
+
+ FROM
+
+
+ COINBASE ( New coins )
+
+
+
+ ) : (
+ <>>
+ )}
+
+
+
Outputs
+
+
+ {(txInfo.outputs || []).map((tx_output) => (
+
+
+ Index
+
+ #{tx_output.index}
+
+
+
+
+ Script Public Key Type
+
+
+ {tx_output.script_public_key_type}
+
+
+
+ Amount
+
+
+ +{numberWithCommas(tx_output.amount / 100000000)}
+ {KASPA_UNIT}
+
+
+
+
+
+ Script Public Key
+
+
+ {tx_output.script_public_key}
+
+
+
+
+ Script Public Key Address
+
+
+
+ {tx_output.script_public_key_address}
+
+
+
+
+ ))}
+
+
+ ) : (
+ <>>
+ )}
+
+
+
-
-
-}
+ );
+};
export default TransactionInfo;
diff --git a/src/components/TxOverview.js b/src/components/TxOverview.js
index 94c60c9..5c38cc3 100644
--- a/src/components/TxOverview.js
+++ b/src/components/TxOverview.js
@@ -1,101 +1,135 @@
-import {useContext, useEffect, useRef, useState} from "react";
-import {OverlayTrigger, Tooltip} from "react-bootstrap";
-import {BiHide} from "react-icons/bi";
-import {FaPause, FaPlay} from "react-icons/fa";
-import {RiMoneyDollarCircleFill} from 'react-icons/ri';
-import {useNavigate} from "react-router-dom";
+import { useContext, useEffect, useRef, useState } from "react";
+import { OverlayTrigger, Tooltip } from "react-bootstrap";
+import { BiHide } from "react-icons/bi";
+import { FaPause, FaPlay } from "react-icons/fa";
+import { RiMoneyDollarCircleFill } from "react-icons/ri";
+import { useNavigate } from "react-router-dom";
import { KASPA_UNIT } from "../explorer_constants";
-import {numberWithCommas} from "../helper";
+import { numberWithCommas } from "../helper";
import LastBlocksContext from "./LastBlocksContext";
-
const TxOverview = (props) => {
+ const [tempBlocks, setTempBlocks] = useState([]);
+ const [keepUpdating, setKeepUpdating] = useState(true);
+ const [ignoreCoinbaseTx, setIgnoreCoinbaseTx] = useState(false);
- const [tempBlocks, setTempBlocks] = useState([]);
- const [keepUpdating, setKeepUpdating] = useState(true);
- const [ignoreCoinbaseTx, setIgnoreCoinbaseTx] = useState(false);
-
- const keepUpdatingRef = useRef()
- keepUpdatingRef.current = keepUpdating
-
- const {blocks, isConnected} = useContext(LastBlocksContext);
- const navigate = useNavigate();
+ const keepUpdatingRef = useRef();
+ keepUpdatingRef.current = keepUpdating;
- const onClickRow = (e) => {
- navigate(`/txs/${e.target.closest("tr").getAttribute("txid")}`)
- }
+ const { blocks, isConnected } = useContext(LastBlocksContext);
+ const navigate = useNavigate();
- const onClickAddr = (e) => {
- navigate(`/addresses/${e.target.closest("tr").getAttribute("id")}`)
- }
+ const onClickRow = (e) => {
+ navigate(`/txs/${e.target.closest("tr").getAttribute("txid")}`);
+ };
- useEffect(() => {
- if (keepUpdatingRef.current) {
- setTempBlocks(blocks);
- }
- }, [blocks])
+ const onClickAddr = (e) => {
+ navigate(`/addresses/${e.target.closest("tr").getAttribute("id")}`);
+ };
- const toggleCoinbaseTransactions = () => {
- setIgnoreCoinbaseTx(!ignoreCoinbaseTx);
+ useEffect(() => {
+ if (keepUpdatingRef.current) {
+ setTempBlocks(blocks);
}
-
-
- return
-
- {!keepUpdating ? setKeepUpdating(true)}/> :
- setKeepUpdating(false)}/>}
-
- {ignoreCoinbaseTx ? "Show" : "Hide"} coinbase
- transactions}>
-
-
-
- LATEST
- TRANSACTIONS
-
-
-
-
-
- Id |
- Amount |
- Recipient |
-
-
-
- {[...tempBlocks]
- .sort((a, b) => b.blueScore - a.blueScore)
- // .slice(0, props.lines)
- .flatMap((block) => block.txs.slice(ignoreCoinbaseTx ? 1 : 0)
- .flatMap((tx) => tx.outputs.flatMap((output, outputIndex) => {
- return {
- "amount": output[1],
- "address": output[0],
- "txId": tx.txId,
- outputIndex
- }
- })))
- .filter((v, i, a) => a.findIndex(v2 => (JSON.stringify(v) === JSON.stringify(v2))) === i)
- .slice(0, props.lines).map(x => {
- return
- {x.txId.slice(0, 10)} |
- {numberWithCommas(x.amount / 100000000)} {KASPA_UNIT}
- |
- {x.address} |
-
- })}
-
-
-
-
-
+ }, [blocks]);
+
+ const toggleCoinbaseTransactions = () => {
+ setIgnoreCoinbaseTx(!ignoreCoinbaseTx);
+ };
+
+ return (
+
+
+ {!keepUpdating ? (
+ setKeepUpdating(true)}
+ />
+ ) : (
+ setKeepUpdating(false)}
+ />
+ )}
+
+
+ {ignoreCoinbaseTx ? "Show" : "Hide"} coinbase transactions
+
+ }
+ >
+
+
+
+
+
+ {" "}
+ LATEST TRANSACTIONS
+
+
+
+
+
+
+ Id |
+ Amount |
+ Recipient |
+
+
+
+ {[...tempBlocks]
+ .sort((a, b) => b.blueScore - a.blueScore)
+ // .slice(0, props.lines)
+ .flatMap((block) =>
+ block.txs.slice(ignoreCoinbaseTx ? 1 : 0).flatMap((tx) =>
+ tx.outputs.flatMap((output, outputIndex) => {
+ return {
+ amount: output[1],
+ address: output[0],
+ txId: tx.txId,
+ outputIndex,
+ };
+ }),
+ ),
+ )
+ .filter(
+ (v, i, a) =>
+ a.findIndex(
+ (v2) => JSON.stringify(v) === JSON.stringify(v2),
+ ) === i,
+ )
+ .slice(0, props.lines)
+ .map((x) => {
+ return (
+
+ {x.txId.slice(0, 10)} |
+
+ {numberWithCommas(x.amount / 100000000)} {KASPA_UNIT}
+ |
+
+ {x.address}
+ |
+
+ );
+ })}
+
+
+
+ );
+};
-}
-
-export default TxOverview
+export default TxOverview;
diff --git a/src/components/TxPage.js b/src/components/TxPage.js
index 393a445..71506fc 100644
--- a/src/components/TxPage.js
+++ b/src/components/TxPage.js
@@ -1,11 +1,14 @@
-import {Container} from 'react-bootstrap'
-import TxOverview from './TxOverview'
+import { Container } from "react-bootstrap";
+import TxOverview from "./TxOverview";
-export default () => {
- return
-
-
-
-
+const QrButton = () => {
+ return (
+
+
+
+
-}
\ No newline at end of file
+ );
+};
+
+export default QrButton;
diff --git a/src/components/UtxoPagination.js b/src/components/UtxoPagination.js
index 6a11dd2..0ed8765 100644
--- a/src/components/UtxoPagination.js
+++ b/src/components/UtxoPagination.js
@@ -1,44 +1,75 @@
-import {Pagination} from "react-bootstrap";
+import { Pagination } from "react-bootstrap";
const UtxoPagination = (props) => {
+ let items = [];
+ let createIndex;
- let items = []
- let createIndex;
-
- if (props.active < 3) {
- createIndex = 3
+ if (props.active < 3) {
+ createIndex = 3;
+ } else {
+ if (props.active > props.total - 2) {
+ createIndex = props.total - 2;
} else {
- if (props.active > props.total - 2) {
- createIndex = props.total - 2
- } else {
- createIndex = props.active;
- }
+ createIndex = props.active;
}
+ }
- for (let i = Math.max(createIndex - 2, 1); i <= Math.min(createIndex + 2, props.total); i++) {
- items.push(
{
- props.setActive(i)
- }}>{i})
- }
+ for (
+ let i = Math.max(createIndex - 2, 1);
+ i <= Math.min(createIndex + 2, props.total);
+ i++
+ ) {
+ items.push(
+
{
+ props.setActive(i);
+ }}
+ >
+ {i}
+ ,
+ );
+ }
- return
-
- {(Math.min(props.active, props.total - 2) - 2) > 1 ? <>
- {
- props.setActive(1)
- }}>{1}
- {props.active > 4 && }
- > : <>>}
- {items}
+ return (
+
+
+ {Math.min(props.active, props.total - 2) - 2 > 1 ? (
+ <>
+ {
+ props.setActive(1);
+ }}
+ >
+ {1}
+
+ {props.active > 4 && }
+ >
+ ) : (
+ <>>
+ )}
+ {items}
- {props.total > Math.max(3, props.active) + 2 ? <>
- {props.active < (props.total - 3) && }
- {
- props.setActive(props.total)
- }}>{props.total}
- > : <>>}
-
+ {props.total > Math.max(3, props.active) + 2 ? (
+ <>
+ {props.active < props.total - 3 &&
}
+
{
+ props.setActive(props.total);
+ }}
+ >
+ {props.total}
+
+ >
+ ) : (
+ <>>
+ )}
+
-}
+ );
+};
-export default UtxoPagination;
\ No newline at end of file
+export default UtxoPagination;
diff --git a/src/explorer_constants.js b/src/explorer_constants.js
index aebc113..d14d2e7 100644
--- a/src/explorer_constants.js
+++ b/src/explorer_constants.js
@@ -1,40 +1,39 @@
let SOCKET_SERVER = process.env.WS_SERVER || "wss://api.kaspa.org";
-let SUFFIX = ""
-let API_SERVER = process.env.REACT_APP_API_SERVER || ""
-let ADDRESS_PREFIX = "kaspa:"
-let KASPA_UNIT = "KAS"
-
-let BPS = 1
+let SUFFIX = "";
+let API_SERVER = process.env.REACT_APP_API_SERVER || "";
+let ADDRESS_PREFIX = "kaspa:";
+let KASPA_UNIT = "KAS";
+let BPS = 1;
switch (process.env.REACT_APP_NETWORK) {
- case "testnet-10":
- SOCKET_SERVER = "wss://api-tn10.kaspa.org";
- ADDRESS_PREFIX = "kaspatest:"
- if (!API_SERVER) {
- API_SERVER = "https://api-tn10.kaspa.org"
- }
- SUFFIX = " TN10"
- KASPA_UNIT = "TKAS"
- break;
- case "testnet-11":
- SOCKET_SERVER = "wss://api-tn11.kaspa.org";
- ADDRESS_PREFIX = "kaspatest:"
- if (!API_SERVER) {
- API_SERVER = "https://api-tn11.kaspa.org"
- }
- SUFFIX = " TN11"
- KASPA_UNIT = "TKAS"
- BPS = 10
- break;
+ case "testnet-10":
+ SOCKET_SERVER = "wss://api-tn10.kaspa.org";
+ ADDRESS_PREFIX = "kaspatest:";
+ if (!API_SERVER) {
+ API_SERVER = "https://api-tn10.kaspa.org";
+ }
+ SUFFIX = " TN10";
+ KASPA_UNIT = "TKAS";
+ break;
+ case "testnet-11":
+ SOCKET_SERVER = "wss://api-tn11.kaspa.org";
+ ADDRESS_PREFIX = "kaspatest:";
+ if (!API_SERVER) {
+ API_SERVER = "https://api-tn11.kaspa.org";
+ }
+ SUFFIX = " TN11";
+ KASPA_UNIT = "TKAS";
+ BPS = 10;
+ break;
- // mainnet
- default:
- SOCKET_SERVER = "wss://api.kaspa.org";
- if (!API_SERVER) {
- API_SERVER = "https://api.kaspa.org"
- }
- break;
+ // mainnet
+ default:
+ SOCKET_SERVER = "wss://api.kaspa.org";
+ if (!API_SERVER) {
+ API_SERVER = "https://api.kaspa.org";
+ }
+ break;
}
-export { SOCKET_SERVER, SUFFIX, API_SERVER, ADDRESS_PREFIX, BPS, KASPA_UNIT }
\ No newline at end of file
+export { SOCKET_SERVER, SUFFIX, API_SERVER, ADDRESS_PREFIX, BPS, KASPA_UNIT };
diff --git a/src/helper.js b/src/helper.js
index fe3b7e5..01cd9b7 100644
--- a/src/helper.js
+++ b/src/helper.js
@@ -1,33 +1,30 @@
-import {useEffect, useRef} from "react";
+import { useEffect, useRef } from "react";
export function numberWithCommas(num) {
- const [integerPart, fractionalPart] = (num || '').toString().split('.');
+ const [integerPart, fractionalPart] = (num || "").toString().split(".");
- if (!integerPart) return "0";
+ if (!integerPart) return "0";
- return (
-
-
- {integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
-
- {fractionalPart &&
- .{fractionalPart}
- }
+ return (
+
+ {integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
+ {fractionalPart && (
+ .{fractionalPart}
+ )}
- );
+ );
}
export function floatToStr(floatNo, maxPrecision = 8) {
- return parseFloat(floatNo.toPrecision(maxPrecision)).toString()
+ return parseFloat(floatNo.toPrecision(maxPrecision)).toString();
}
-
export function usePrevious(value) {
- const ref = useRef();
- useEffect(() => {
- ref.current = value; //assign the value of ref to the argument
- }, [value]); //this code will run when the value of 'value' changes
- return ref.current; //in the end, return the current ref value.
+ const ref = useRef();
+ useEffect(() => {
+ ref.current = value; //assign the value of ref to the argument
+ }, [value]); //this code will run when the value of 'value' changes
+ return ref.current; //in the end, return the current ref value.
}
-export default usePrevious;
\ No newline at end of file
+export default usePrevious;
diff --git a/src/index.js b/src/index.js
index f180c33..175fcfd 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,12 +1,12 @@
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-import './index.css';
-import App from './App';
-import {BrowserRouter} from "react-router-dom";
+import React from "react";
+import ReactDOM from "react-dom/client";
+import "./index.css";
+import App from "./App";
+import { BrowserRouter } from "react-router-dom";
-const root = ReactDOM.createRoot(document.getElementById('root'));
+const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
-
-
-
+
+
+ ,
);
diff --git a/src/inscriptions.js b/src/inscriptions.js
index caeeceb..7642712 100644
--- a/src/inscriptions.js
+++ b/src/inscriptions.js
@@ -1,73 +1,75 @@
export const parseSignatureScript = (hex) => {
- /* Decodes KRC-20 operations */
- /* Example tx deploy: 105424172e946f85daf87f50422e4a5a7f73d541a7a0eaf666cf40567a80ba5d */
- /* Example tx mint: e2792d606f7cae9994cbc233a2cdb9c4d4541ad195852ae8ecfd787254613ded */
- /* Example tx transfer: 657a7a3dbe5c6af4e49eba4c0cf82753f01d5e721f458d356f62bebd0afd54c8 */
- if (!hex) {
- return;
- }
- return parseSignature(hexToBytes(hex));
-}
+ /* Decodes KRC-20 operations */
+ /* Example tx deploy: 105424172e946f85daf87f50422e4a5a7f73d541a7a0eaf666cf40567a80ba5d */
+ /* Example tx mint: e2792d606f7cae9994cbc233a2cdb9c4d4541ad195852ae8ecfd787254613ded */
+ /* Example tx transfer: 657a7a3dbe5c6af4e49eba4c0cf82753f01d5e721f458d356f62bebd0afd54c8 */
+ if (!hex) {
+ return;
+ }
+ return parseSignature(hexToBytes(hex));
+};
function parseSignature(bytes) {
- let result = [];
+ let result = [];
- let offset = 0;
- while (offset < bytes.length) {
- const opcode = bytes[offset];
- offset += 1;
- if (opcode >= 0x01 && opcode <= 0x4b) {
- const dataLength = opcode;
- const data = bytes.slice(offset, offset + dataLength);
- if (isHumanReadable(data)) {
- result.push(`OP_PUSH ${bytesToString(data)}`);
- } else {
- result.push(`OP_PUSH ${bytesToHex(data)}`);
- }
- offset += dataLength;
- } else if (opcode === 0x4c) {
- const dataLength = bytes[offset];
- offset += 1;
- const data = bytes.slice(offset, offset + dataLength);
- if (isHumanReadable(data)) {
- result.push(`OP_PUSHDATA1 ${bytesToString(data)}`);
- } else {
- result = result.concat(parseSignature(data));
- }
- offset += dataLength;
- } else if (opcode === 0x00) {
- result.push('OP_0')
- } else if (opcode === 0x51) {
- result.push('OP_1')
- } else if (opcode === 0x63) {
- result.push('OP_IF')
- } else if (opcode === 0x68) {
- result.push('OP_ENDIF')
- } else if (opcode === 0xac) {
- result.push('OP_CHECKSIG')
- } else {
- result.push(`OP_UNKNOWN ${bytesToHex(opcode)}`)
- }
+ let offset = 0;
+ while (offset < bytes.length) {
+ const opcode = bytes[offset];
+ offset += 1;
+ if (opcode >= 0x01 && opcode <= 0x4b) {
+ const dataLength = opcode;
+ const data = bytes.slice(offset, offset + dataLength);
+ if (isHumanReadable(data)) {
+ result.push(`OP_PUSH ${bytesToString(data)}`);
+ } else {
+ result.push(`OP_PUSH ${bytesToHex(data)}`);
+ }
+ offset += dataLength;
+ } else if (opcode === 0x4c) {
+ const dataLength = bytes[offset];
+ offset += 1;
+ const data = bytes.slice(offset, offset + dataLength);
+ if (isHumanReadable(data)) {
+ result.push(`OP_PUSHDATA1 ${bytesToString(data)}`);
+ } else {
+ result = result.concat(parseSignature(data));
+ }
+ offset += dataLength;
+ } else if (opcode === 0x00) {
+ result.push("OP_0");
+ } else if (opcode === 0x51) {
+ result.push("OP_1");
+ } else if (opcode === 0x63) {
+ result.push("OP_IF");
+ } else if (opcode === 0x68) {
+ result.push("OP_ENDIF");
+ } else if (opcode === 0xac) {
+ result.push("OP_CHECKSIG");
+ } else {
+ result.push(`OP_UNKNOWN ${bytesToHex(opcode)}`);
}
- return result;
+ }
+ return result;
}
const hexToBytes = (hex) => {
- const bytes = [];
- for (let i = 0; i < hex.length; i += 2) {
- bytes.push(parseInt(hex.substring(i, i + 2), 16));
- }
- return new Uint8Array(bytes);
+ const bytes = [];
+ for (let i = 0; i < hex.length; i += 2) {
+ bytes.push(parseInt(hex.substring(i, i + 2), 16));
+ }
+ return new Uint8Array(bytes);
};
function isHumanReadable(bytes) {
- return bytes.every(byte => byte >= 0x20 && byte <= 0x7E);
+ return bytes.every((byte) => byte >= 0x20 && byte <= 0x7e);
}
const bytesToHex = (bytes) => {
- return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
-}
+ return Array.from(bytes)
+ .map((b) => b.toString(16).padStart(2, "0"))
+ .join("");
+};
const bytesToString = (bytes) => {
- return new TextDecoder().decode(bytes);
-}
+ return new TextDecoder().decode(bytes);
+};
diff --git a/src/kaspa-api-client.js b/src/kaspa-api-client.js
index 40ba9fb..00aabac 100644
--- a/src/kaspa-api-client.js
+++ b/src/kaspa-api-client.js
@@ -1,142 +1,169 @@
-import {API_SERVER} from "./explorer_constants";
+import { API_SERVER } from "./explorer_constants";
-const API_BASE = API_SERVER + "/"
+const API_BASE = API_SERVER + "/";
export async function getBlock(hash) {
- const res = await fetch(`${API_BASE}blocks/${hash}?includeColor=true`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}blocks/${hash}?includeColor=true`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getTransaction(hash) {
- const res = await fetch(`${API_BASE}transactions/${hash}`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}transactions/${hash}`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getBlockdagInfo() {
- const res = await fetch(`${API_BASE}info/blockdag`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}info/blockdag`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getKaspadInfo() {
- const res = await fetch(`${API_BASE}info/kaspad`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}info/kaspad`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getHashrateMax() {
- const res = await fetch(`${API_BASE}info/hashrate/max`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}info/hashrate/max`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getFeeEstimate() {
- const res = await fetch(`${API_BASE}info/fee-estimate`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}info/fee-estimate`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getCoinSupply() {
- const res = await fetch(`${API_BASE}info/coinsupply`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}info/coinsupply`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getAddressBalance(addr) {
- const res = await fetch(`${API_BASE}addresses/${addr}/balance`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data.balance
- })
- return res
+ const res = await fetch(`${API_BASE}addresses/${addr}/balance`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data.balance;
+ });
+ return res;
}
-
export async function getAddressTxCount(addr) {
- const res = await fetch(`${API_BASE}addresses/${addr}/transactions-count`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data.total
- })
- return res
+ const res = await fetch(`${API_BASE}addresses/${addr}/transactions-count`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data.total;
+ });
+ return res;
}
-
export async function getAddressUtxos(addr) {
- const res = await fetch(`${API_BASE}addresses/${addr}/utxos`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}addresses/${addr}/utxos`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getAddressName(addr) {
- const res = await fetch(`${API_BASE}addresses/${addr}/name`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}addresses/${addr}/name`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
-
export async function getHalving() {
- const res = await fetch(`${API_BASE}info/halving`, {headers: {'Access-Control-Allow-Origin': '*'}})
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}info/halving`, {
+ headers: { "Access-Control-Allow-Origin": "*" },
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
export async function getTransactionsFromAddress(addr, limit = 20, offset = 0) {
- const res = await fetch(`${API_BASE}addresses/${addr}/full-transactions?limit=${limit}&offset=${offset}`, {
- headers: {
- 'Access-Control-Allow-Origin': '*', 'content-type': 'application/json'
- }, method: "GET"
- })
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(
+ `${API_BASE}addresses/${addr}/full-transactions?limit=${limit}&offset=${offset}`,
+ {
+ headers: {
+ "Access-Control-Allow-Origin": "*",
+ "content-type": "application/json",
+ },
+ method: "GET",
+ },
+ )
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
-
export async function getTransactions(tx_list, inputs, outputs) {
- const res = await fetch(`${API_BASE}transactions/search`, {
- headers: {
- 'Access-Control-Allow-Origin': '*', 'content-type': 'application/json'
- }, method: "POST", body: JSON.stringify({"transactionIds": tx_list})
- })
- .then((response) => response.json())
- .then(data => {
- return data
- })
- return res
+ const res = await fetch(`${API_BASE}transactions/search`, {
+ headers: {
+ "Access-Control-Allow-Origin": "*",
+ "content-type": "application/json",
+ },
+ method: "POST",
+ body: JSON.stringify({ transactionIds: tx_list }),
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ return data;
+ });
+ return res;
}
-
From a7f1a61523d455593c1bea9a86e1c97a43a0a90b Mon Sep 17 00:00:00 2001
From: x100111010 <167847953+x100111010@users.noreply.github.com>
Date: Tue, 22 Oct 2024 02:08:59 +0200
Subject: [PATCH 3/3] fix crashing
- `==` checks equality with type coercion (`5 == '5'` is `true`)
- `===` checks equality without type coercion (`5 === '5'` is `false`)
- added `?.` to prevent crashes when objects may be `undefined` or `null`
Warnings:
- `==` causes build warnings
---
src/components/AddressInfo.js | 59 ++++++++++++-------------------
src/components/TransactionInfo.js | 34 ++++++++----------
2 files changed, 37 insertions(+), 56 deletions(-)
diff --git a/src/components/AddressInfo.js b/src/components/AddressInfo.js
index 89bd4dc..1cfc67c 100644
--- a/src/components/AddressInfo.js
+++ b/src/components/AddressInfo.js
@@ -93,19 +93,12 @@ const AddressInfo = () => {
e.preventDefault();
};
- const getAddrFromOutputs = (outputs, i) => {
- for (const o of outputs) {
- if (o.index === i) {
- return o.script_public_key_address;
- }
- }
+ const getAddrFromOutputs = (outputs, index) => {
+ return outputs?.[index]?.script_public_key_address;
};
- const getAmountFromOutputs = (outputs, i) => {
- for (const o of outputs) {
- if (o.index === i) {
- return o.amount / 100000000;
- }
- }
+
+ const getAmountFromOutputs = (outputs, index) => {
+ return outputs?.[index]?.amount / 100000000;
};
const getAmount = (outputs, inputs) => {
@@ -125,7 +118,7 @@ const AddressInfo = () => {
balance =
balance -
getAmountFromOutputs(
- txsInpCache[i.previous_outpoint_hash]["outputs"],
+ txsInpCache[i.previous_outpoint_hash]?.outputs,
i.previous_outpoint_index,
);
}
@@ -498,8 +491,18 @@ const AddressInfo = () => {
>
{x.inputs?.length > 0
? x.inputs.map((x) => {
- return txsInpCache &&
- txsInpCache[x.previous_outpoint_hash] ? (
+ const inputOutputs =
+ txsInpCache[x.previous_outpoint_hash]
+ ?.outputs || [];
+ const address = getAddrFromOutputs(
+ inputOutputs,
+ x.previous_outpoint_index,
+ );
+ const amount = getAmountFromOutputs(
+ inputOutputs,
+ x.previous_outpoint_index,
+ );
+ return address ? (
<>
{
>
- {getAddrFromOutputs(
- txsInpCache[
- x.previous_outpoint_hash
- ]["outputs"],
- x.previous_outpoint_index,
- )}
+ {address}
- -
- {numberWithCommas(
- getAmountFromOutputs(
- txsInpCache[
- x.previous_outpoint_hash
- ]["outputs"],
- x.previous_outpoint_index,
- ),
- )}
+ -{numberWithCommas(amount)}
{KASPA_UNIT}
diff --git a/src/components/TransactionInfo.js b/src/components/TransactionInfo.js
index f4a29d3..346ff5e 100644
--- a/src/components/TransactionInfo.js
+++ b/src/components/TransactionInfo.js
@@ -11,11 +11,7 @@ import CopyButton from "./CopyButton.js";
import { parseSignatureScript } from "../inscriptions";
const getOutputFromIndex = (outputs, index) => {
- for (const output of outputs) {
- if (output.index === index) {
- return output;
- }
- }
+ return outputs[index];
};
const TransactionInfo = () => {
@@ -24,7 +20,7 @@ const TransactionInfo = () => {
// const [storageMass, setStorageMass] = useState();
const [additionalTxInfo, setAdditionalTxInfo] = useState();
const [showTxFee, setShowTxFee] = useState(false);
- const setError = useState(false);
+ const [, setError] = useState(false);
const retryCnt = useRef(0);
const retryNotAccepted = useRef(6);
@@ -40,7 +36,7 @@ const TransactionInfo = () => {
setTxInfo(undefined);
throw err;
});
- }, [id, setError]);
+ }, [id]);
useEffect(() => {
setError(false);
@@ -201,12 +197,12 @@ const TransactionInfo = () => {
additionalTxInfo[
tx_input.previous_outpoint_hash
]?.outputs || [],
- tx_input.previous_outpoint_index,
+ tx_input?.previous_outpoint_index,
)?.amount || 0,
)
.reduce((a, b) => a + b) -
(
- txInfo.outputs?.map((v) => v.amount) || [0]
+ txInfo.outputs?.map((v) => v?.amount) || [0]
).reduce((a, b) => a + b)) /
100000000}{" "}
{KASPA_UNIT}
@@ -338,8 +334,8 @@ const TransactionInfo = () => {
additionalTxInfo[
tx_input.previous_outpoint_hash
].outputs,
- tx_input.previous_outpoint_index,
- ).amount / 100000000,
+ tx_input?.previous_outpoint_index,
+ )?.amount / 100000000,
)}
{KASPA_UNIT}
@@ -351,7 +347,7 @@ const TransactionInfo = () => {
Previous Outpoint Index + Hash
- #{tx_input.previous_outpoint_index}{" "}
+ #{tx_input?.previous_outpoint_index}{" "}
{tx_input.previous_outpoint_hash}
@@ -369,8 +365,8 @@ const TransactionInfo = () => {
additionalTxInfo[
tx_input.previous_outpoint_hash
].outputs,
- tx_input.previous_outpoint_index,
- ).script_public_key_address
+ tx_input?.previous_outpoint_index,
+ )?.script_public_key_address
}`}
className="blockinfo-link"
>
@@ -379,8 +375,8 @@ const TransactionInfo = () => {
additionalTxInfo[
tx_input.previous_outpoint_hash
].outputs,
- tx_input.previous_outpoint_index,
- ).script_public_key_address
+ tx_input?.previous_outpoint_index,
+ )?.script_public_key_address
}
@@ -429,7 +425,7 @@ const TransactionInfo = () => {
Amount
- +{numberWithCommas(tx_output.amount / 100000000)}
+ +{numberWithCommas(tx_output?.amount / 100000000)}
{KASPA_UNIT}
@@ -448,10 +444,10 @@ const TransactionInfo = () => {
- {tx_output.script_public_key_address}
+ {tx_output?.script_public_key_address}