API v2.0.0

- all actions return JSON
- condense some actions
- add /api/blockchain/next-halving

https://github.com/janoside/btc-rpc-explorer/discussions/520
This commit is contained in:
Dan Janosik 2023-04-05 16:56:40 -04:00
parent 8950c95d56
commit 09a31f1e78
No known key found for this signature in database
GPG key ID: 70C0B166321C0AF8
6 changed files with 247 additions and 231 deletions

View file

@ -1,35 +1,51 @@
This changelog specifically tracks changes to the Public API available at `/api` and is maintained separately from the app CHANGELOG such that it can properly adhere to semantic versioning.
##### v1.2.0
##### v2.0.0
###### Unreleased
* Added: /api/xyzpub/txids/:xyzpub
* Added: /api/xyzpub/addresses/:xyzpub
* Added: /api/block/header/:height
* Added: /api/block/header/:hash
* Added: /api/holidays/all
* Added: /api/holidays/today
* Added: /api/holidays/:day
* Added: /api/tx/volume/24h
* Changed: /api/tx/:txid
* Added result.vin[i].scriptSig.address
* Added result.vin[i].scriptSig.type
* Added result.fee, including result.fee.amount and result.fee.unit
* Added result.fun, when applicable, which includes special details about the tx
* Changed path: /api/util/xyzpub/:xyzpub -> /api/xyzpub/:xyzpub (auto-redirect included)
* Changed: /api/price[/...]
* Return values exclude thousands separators by default; they can be added with "?format=true"
* BREAKING: All actions now return JSON content
* Added:
* `/api/blocks/tip` (replaces `/api/blocks/tip/hash` and `/api/blocks/tip/height`)
* `/api/xyzpub/txids/$XPUB`
* `/api/xyzpub/addresses/$XPUB`
* `/api/block/header/$HEIGHT`
* `/api/block/header/$HASH`
* `/api/blockchain/next-halving`
* `/api/holidays/all`
* `/api/holidays/today`
* `/api/holidays/$DAY`
* `/api/tx/volume/24h`
* `/api/price/marketcap` (replaces `/api/price/$CURRENCY/marketcap`)
* `/api/price/sats` (replaces `/api/price/$CURRENCY/sats`)
* Changed output:
* `/api/tx/$TXID`
* Added result.vin[i].scriptSig.address
* Added result.vin[i].scriptSig.type
* Added result.fee, including result.fee.amount and result.fee.unit
* Added result.fun, when applicable, which includes special details about the tx
* `/api/price[/...]`
* Return values exclude thousands separators by default; they can be added with "?format=true"
* Changed path:
* `/api/util/xyzpub/$XPUB` -> `/api/xyzpub/$XPUB` (auto-redirect included)
* Removed:
* `/api/blocks/tip/hash` (see `/api/blocks/tip`)
* `/api/blocks/tip/height` (see `/api/blocks/tip`)
* `/api/mempool/count` (see "size" field in output from `/api/mempool/summary`)
* `/api/price/$CURRENCY/marketcap` (see individual fields in output from `/api/price/marketcap`)
* `/api/price/$CURRENCY/sats` (see individual fields in output from `/api/price/sats`)
##### v1.1.0
###### 2021-12-07
* Added: /api/blockchain/utxo-set
* Added: /api/address/:address
* Added: /api/mining/next-block
* Added: /api/mining/next-block/txids
* Added: /api/mining/next-block/includes/:txid
* Added: /api/mining/miner-summary
* Added:
* `/api/blockchain/utxo-set`
* `/api/address/$ADDRESS`
* `/api/mining/next-block`
* `/api/mining/next-block/txids`
* `/api/mining/next-block/includes/$TXID`
* `/api/mining/miner-summary`

View file

@ -1,6 +1,7 @@
##### v3.4.0
###### Unreleased (beta)
* Breaking changes to the API (see [./api/changelog](/api/changlog))
* Include median fee rate for next-block estimates, on homepage and at [/next-block](./next-block)
* Minor fixes for running against Bitcoin Core v23
* Block Analysis: include top "days destroyed" transactions

View file

@ -8,6 +8,8 @@ const crypto = require('crypto');
const url = require('url');
const path = require('path');
const apiDocs = require("../docs/api.js");
let baseUrl = (process.env.BTCEXP_BASEURL || "/").trim();
if (!baseUrl.startsWith("/")) {
baseUrl = "/" + baseUrl;
@ -90,6 +92,7 @@ module.exports = {
secureSite: process.env.BTCEXP_SECURE_SITE == "true",
baseUrl: baseUrl,
apiBaseUrl: apiDocs.baseUrl,
coin: currentCoin,
@ -252,7 +255,7 @@ module.exports = {
/* 14 */ {name:"Predicted Blocks", url:"./predicted-blocks", desc:"View predicted future blocks based on the current mempool.", iconClass:"bi-arrow-right-circle"},
/* 15 */ {name:"API", url:"./api/docs", desc:"View docs for the public API.", iconClass:"bi-braces-asterisk"},
/* 15 */ {name:"API", url:`.${apiDocs.baseUrl}/docs`, desc:"View docs for the public API.", iconClass:"bi-braces-asterisk"},
/* 16 */ {name:"Next Block", url:"./next-block", desc:"View a prediction for the next block, based on the current mempool.", iconClass:"bi-minecart-loaded"},
/* 17 */ {name:"Quotes", url:"./quotes", desc:"Curated list of Bitcoin-related quotes.", iconClass:"bi-chat-quote"},

View file

@ -1,52 +1,41 @@
module.exports = {
"version": "1.2.0",
"version": "2.0.0",
"baseUrl": "/api",
"routes":[
// blocks
{
"category":"blocks",
"url":"/api/block/:hash",
"url":"/block/$HASH",
"desc":"Returns the details of the block with the given hash.",
"returnType":"json",
"testUrl":"/api/block/0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893"
"testUrl":"/block/0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893"
},
{
"category":"blocks",
"url":"/api/block/:height",
"url":"/block/$HEIGHT",
"desc":"Returns the details of the block at the given height.",
"returnType":"json",
"testUrl":"/api/block/123456"
"testUrl":"/block/123456"
},
{
"category":"blocks",
"url":"/api/block/header/:hash",
"url":"/block/header/$HASH",
"desc":"Returns the details of the block header with the given hash.",
"returnType":"json",
"testUrl":"/api/block/header/0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893"
"testUrl":"/block/header/0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893"
},
{
"category":"blocks",
"url":"/api/block/header/:height",
"url":"/block/header/$HEIGHT",
"desc":"Returns the details of the block header at the given height.",
"returnType":"json",
"testUrl":"/api/block/header/123456"
"testUrl":"/block/header/123456"
},
{
"category":"blocks",
"url":"/api/blocks/tip/height",
"desc":"Returns the height of the chain tip.",
"returnType":"integer"
},
{
"category":"blocks",
"url":"/api/blocks/tip/hash",
"desc":"Returns the block hash of the chain tip.",
"returnType":"string"
"url":"/blocks/tip",
"desc":"Returns basic details about the chain tip."
},
@ -55,17 +44,15 @@ module.exports = {
// transactions
{
"category":"transactions",
"url":"/api/tx/:txid",
"url":"/tx/$TXID",
"desc":"Returns the details of the transaction with the given txid.",
"returnType":"json",
"testUrl": "/api/tx/f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"
"testUrl": "/tx/f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"
},
{
"category":"transactions",
"url":"/api/tx/volume/24h",
"url":"/tx/volume/24h",
"desc":"Returns total output of all transactions over the last 24hrs.",
"returnType":"json",
"testUrl": "/api/tx/volume/24h",
"testUrl": "/tx/volume/24h",
"hideInSlowMode": true
},
@ -75,15 +62,18 @@ module.exports = {
// blockchain
{
"category":"blockchain",
"url":"/api/blockchain/coins",
"desc":"Returns the current supply of Bitcoin. An estimate using a checkpoint can be returned in 2 cases: on 'slow' devices, and before the UTXO Set snapshot is loaded.",
"returnType":"number"
"url":"/blockchain/coins",
"desc":"Returns the current supply of Bitcoin. An estimate using a checkpoint can be returned in 2 cases: on 'slow' devices, and before the UTXO Set snapshot is loaded."
},
{
"category":"blockchain",
"url":"/api/blockchain/utxo-set",
"desc":"Returns the latest UTXO Set snapshot. Warning: This call can be very slow, depending on node hardware and index configurations.",
"returnType":"json"
"url":"/blockchain/utxo-set",
"desc":"Returns the latest UTXO Set snapshot. Warning: This call can be very slow, depending on node hardware and index configurations."
},
{
"category":"blockchain",
"url":"/blockchain/next-halving",
"desc":"Returns details about the next, upcoming halving."
},
@ -93,15 +83,14 @@ module.exports = {
// addresses
{
"category":"addresses",
"url":"/api/address/:address",
"desc":"Returns a summary of data pertaining to the given address. The output of this call will depend heavily on the configured 'Address API' (see .env-sample file).",
"url":"/address/$ADDRESS",
"desc":"Returns a summary of data pertaining to the given address. The output of this call will depend heavily on the configured 'Address API' (see .env-sample file). If an Address API is configured, transactions related to the given address will be returned (the below optional parameters apply to those transactions).",
"optionalParams": {
"limit":"Number of transactions to return",
"offset":"Offset into transactions",
"sort":"Sorting direction for transactions ('desc'=new first, 'asc'=old first)"
},
"returnType":"json",
"testUrl":"/api/address/34rng4QwB5pHUbGDJw1JxjLwgEU8TQuEqv"
"testUrl":"/address/34rng4QwB5pHUbGDJw1JxjLwgEU8TQuEqv"
},
@ -111,37 +100,34 @@ module.exports = {
// xyz-pubs
{
"category":"xpubs",
"url":"/api/xyzpub/:extendedPubkey",
"url":"/xyzpub/$XPUB",
"desc":"Returns details for the specified extended public key, including related keys and addresses.",
"returnType":"json",
"optionalParams": {
"limit":"The number of addresses to return",
"offset":"Offset into the list of addresses"
},
"testUrl": "/api/util/xyzpub/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd"
"testUrl": "/util/xyzpub/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd"
},
{
"category":"xpubs",
"url":"/api/xyzpub/addresses/:xyzpub",
"url":"/xyzpub/addresses/$XPUB",
"desc":"Returns a list of addresses derived from the given [xyz]pub.",
"optionalParams": {
"receiveOrChange":"0 for 'receive' addresses (default); 1 for 'change' addresses",
"limit":"Number of addresses to return",
"offset":"Offset into addresses"
},
"returnType":"json",
"testUrl":"/api/xyzpub/addresses/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd"
"testUrl":"/xyzpub/addresses/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd"
},
{
"category":"xpubs",
"url":"/api/xyzpub/txids/:xyzpub",
"url":"/xyzpub/txids/$XPUB",
"desc":"Returns a list of transaction IDs associated with the given [xyz]pub.",
"optionalParams": {
"gapLimit":"Limit of empty addresses to end searching for transactions (default: 20)",
"addressLimit":"Forced limit on the number of addresses to search through (both 'receive' and 'change' addresses up to this number will be searched)"
},
"returnType":"json",
"testUrl":"/api/xyzpub/txids/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd"
"testUrl":"/xyzpub/txids/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd"
},
@ -150,45 +136,39 @@ module.exports = {
// mining
{
"category":"mining",
"url":"/api/mining/hashrate",
"desc":"Returns the network hash rate, estimated over the last 1, 7, 30, 90, and 365 days.",
"returnType":"json"
"url":"/mining/hashrate",
"desc":"Returns the network hash rate, estimated over the last 1, 7, 30, 90, and 365 days."
},
{
"category":"mining",
"url":"/api/mining/diff-adj-estimate",
"desc":"Returns the current estimate for the next difficulty adjustment as a percentage.",
"returnType":"number"
"url":"/mining/diff-adj-estimate",
"desc":"Returns the current estimate for the next difficulty adjustment as a percentage."
},
{
"category":"mining",
"url":"/api/mining/next-block",
"desc":"Returns a summary for the estimated next block to be mined (produced via getblocktemplate).",
"returnType":"json"
"url":"/mining/next-block",
"desc":"Returns a summary for the estimated next block to be mined (produced via getblocktemplate)."
},
{
"category":"mining",
"url":"/api/mining/next-block/txids",
"desc":"Returns a list of the transaction IDs included in the estimated next block to be mined (produced via getblocktemplate).",
"returnType":"json"
"url":"/mining/next-block/txids",
"desc":"Returns a list of the transaction IDs included in the estimated next block to be mined (produced via getblocktemplate)."
},
{
"category":"mining",
"url":"/api/mining/next-block/includes/:txid",
"url":"/mining/next-block/includes/$TXID",
"desc":"Returns whether the specified transaction ID is included in the estimated next block to be mined (produced via getblocktemplate).",
"returnType":"boolean",
"testUrl":"/api/mining/next-block/includes/yourTxId"
"testUrl":"/mining/next-block/includes/yourTxId"
},
{
"category":"mining",
"url":"/api/mining/miner-summary",
"url":"/mining/miner-summary",
"desc":"Returns whether the specified transaction ID is included in the estimated next block to be mined (produced via getblocktemplate).",
"returnType":"json",
"optionalParams": {
"since":"Use the form 'Nd' to specify the number of day to look back (e.g. 'since=7d' will analyze the last 7 days)",
"startHeight+endHeight":"Use these 2 parameters to specify a custom start/end height (e.g. 'startHeight=0&endHeight=24' to analyze the first 25 blocks)"
},
"testUrl":"/api/mining/miner-summary?since=1d"
"testUrl":"/mining/miner-summary?since=1d"
},
@ -198,22 +178,19 @@ module.exports = {
// mempool
{
"category":"mempool",
"url":"/api/mempool/count",
"desc":"Returns the number of transactions in Bitcoin Core's mempool.",
"returnType":"integer"
"url":"/mempool/count",
"desc":"Returns the number of transactions in Bitcoin Core's mempool."
},
{
"category":"mempool",
"url":"/api/mempool/summary",
"url":"/mempool/summary",
"desc":"Returns a summary of Bitcoin Core's mempool (full output from 'getmempoolinfo')",
"returnType":"json",
"example": {"loaded":true,"size":225,"bytes":76209,"usage":410496,"total_fee":0.01763495,"maxmempool":15000000,"mempoolminfee":0.00001,"minrelaytxfee":0.00001,"unbroadcastcount":0}
},
{
"category":"mempool",
"url":"/api/mempool/fees",
"url":"/mempool/fees",
"desc":"Returns recommended fee rates in sats/vB for next block, ~30 min, 1 hr, and 1 day.",
"returnType":"json",
"example": {"nextBlock":17,"30min":9,"60min":9,"1day":9}
},
@ -222,39 +199,21 @@ module.exports = {
// price
{
"category":"price",
"url":"/api/price",
"url":"/price",
"desc":"Returns the price of 1 BTC, in USD, EUR, GBP, and XAU",
"returnType":"json",
"optionalParams": {
"format":"Set to 'true' to include thousands-separator formatting in results"
}
},
{
"category":"price",
"url":"/api/price/:currency",
"desc":"Returns the price of 1 BTC, in one of USD, EUR, GBP, XAU",
"params":[{name: "currency", "options": ["usd", "eur", "gbp", "xau"]}],
"returnType":"number",
"testUrl": "/api/price/usd",
"optionalParams": {
"format":"Set to 'true' to include thousands-separator formatting in results"
}
"url":"/price/marketcap",
"desc":"Returns the market cap of Bitcoin, in USD, EUR, GBP, XAU",
},
{
"category":"price",
"url":"/api/price/:currency/marketcap",
"desc":"Returns the market cap of Bitcoin, in one of USD, EUR, GBP, XAU",
"params":[{name: "currency", "options": ["usd", "eur", "gbp", "xau"]}],
"returnType":"number",
"testUrl": "/api/price/usd/marketcap"
},
{
"category":"price",
"url":"/api/price/:currency/sats",
"desc":"Returns the price of 1 unit of the specified currency (e.g. 1 \"usd\") in satoshis",
"params":[{name: "currency", "options": ["usd", "eur", "gbp", "xau"]}],
"returnType":"number",
"testUrl": "/api/price/usd/sats"
"url":"/price/sats",
"desc":"Returns the price of 1 unit of [USD, EUR, GBP, XAU] (e.g. 1 \"usd\") in satoshis (aka \"Moscow Time\")",
},
@ -263,45 +222,39 @@ module.exports = {
// fun
{
"category":"fun",
"url":"/api/quotes/all",
"url":"/quotes/all",
"desc":"Returns the full curated list of Bitcoin quotes.",
"returnType":"json"
},
{
"category":"fun",
"url":"/api/quotes/:index",
"url":"/quotes/$INDEX",
"desc":"Returns the Bitcoin quote with the given index from the curated list.",
"returnType":"json",
"testUrl": "/api/quotes/0"
"testUrl": "/quotes/0"
},
{
"category":"fun",
"url":"/api/quotes/random",
"desc":"Returns a random Bitcoin quote from the curated list.",
"returnType":"json"
"url":"/quotes/random",
"desc":"Returns a random Bitcoin quote from the curated list."
},
{
"category":"fun",
"url":"/api/holidays/all",
"desc":"Returns the full curated list of Bitcoin Holidays.",
"returnType":"json"
"url":"/holidays/all",
"desc":"Returns the full curated list of Bitcoin Holidays."
},
{
"category":"fun",
"url":"/api/holidays/today",
"url":"/holidays/today",
"desc":"Returns the Bitcoin Holidays celebrated 'today' (i.e. at the time the API call is made).",
"optionalParams": {
"tzOffset":"The number of hours to offset from UTC for the caller's local timezone, e.g. \"-5\" for EST"
},
"returnType":"json",
"testUrl": "/api/holidays/today?tzOffset=-5"
"testUrl": "/holidays/today?tzOffset=-5"
},
{
"category":"fun",
"url":"/api/holidays/:day",
"url":"/holidays/$DAY",
"desc":"Returns the Bitcoin Holidays celebrated on the specified day, using one of the following formats: yyyy-MM-DD, MM-DD.",
"returnType":"json",
"testUrl": "/api/holidays/01-03"
"testUrl": "/holidays/01-03"
},
@ -310,9 +263,8 @@ module.exports = {
// admin
{
"category":"admin",
"url":"/api/version",
"desc":"Returns the semantic version of the public API, which is maintained separate from the app version.",
"returnType":"string",
"url":"/version",
"desc":"Returns the semantic version of the public API, which is maintained separate from the app version."
},
]

View file

@ -34,6 +34,7 @@ const forceCsrf = csurf({ ignoreMethods: [] });
router.get("/docs", function(req, res, next) {
res.locals.apiDocs = apiDocs;
res.locals.apiBaseUrl = apiDocs.baseUrl;
res.locals.route = req.query.route;
res.locals.categories = [];
@ -73,11 +74,14 @@ router.get("/version", function(req, res, next) {
/// BLOCKS
router.get("/blocks/tip/height", asyncHandler(async (req, res, next) => {
router.get("/blocks/tip", asyncHandler(async (req, res, next) => {
try {
const blockcount = await rpcApi.getBlockCount();
const getblockchaininfo = await coreApi.getBlockchainInfo();
res.send(blockcount.toString());
res.send({
height: getblockchaininfo.blocks,
hash: getblockchaininfo.bestblockhash
});
} catch (e) {
utils.logError("a39gfoeuew", e);
@ -88,12 +92,6 @@ router.get("/blocks/tip/height", asyncHandler(async (req, res, next) => {
next();
}));
router.get("/blocks/tip/hash", function(req, res, next) {
coreApi.getBlockchainInfo().then(function(getblockchaininfo){
res.send(getblockchaininfo.bestblockhash.toString());
}).catch(next);
});
router.get("/block/:hashOrHeight", asyncHandler(async (req, res, next) => {
const hashOrHeight = req.params.hashOrHeight;
let hash = (hashOrHeight.length == 64 ? hashOrHeight : null);
@ -221,22 +219,33 @@ router.get("/tx/volume/24h", function(req, res, next) {
/// BLOCKCHAIN
router.get("/blockchain/coins", function(req, res, next) {
router.get("/blockchain/coins", asyncHandler(async (req, res, next) => {
if (global.utxoSetSummary) {
let supply = parseFloat(global.utxoSetSummary.total_amount).toString();
res.send(supply.toString());
res.send({
supply: supply.toString(),
type: "calculated"
});
next();
} else {
// estimated supply
coreApi.getBlockchainInfo().then(function(getblockchaininfo){
let estimatedSupply = utils.estimatedSupply(getblockchaininfo.blocks);
res.send(estimatedSupply.toString());
}).catch(next);
let getblockchaininfo = await coreApi.getBlockchainInfo();
let estimatedSupply = utils.estimatedSupply(getblockchaininfo.blocks);
let lastCheckpoint = coinConfig.utxoSetCheckpointsByNetwork[global.activeBlockchain];
res.send({
supply: estimatedSupply.toString(),
type: "estimated",
lastCheckpointHeight: lastCheckpoint.height
})
next();
}
});
}));
router.get("/blockchain/utxo-set", asyncHandler(async (req, res, next) => {
const utxoSetSummary = await coreApi.getUtxoSetSummary(true, true);
@ -246,6 +255,68 @@ router.get("/blockchain/utxo-set", asyncHandler(async (req, res, next) => {
next();
}));
router.get("/blockchain/next-halving", asyncHandler(async (req, res, next) => {
try {
const getblockchaininfo = await coreApi.getBlockchainInfo();
let promises = [];
res.locals.getblockchaininfo = getblockchaininfo;
res.locals.difficultyPeriod = parseInt(Math.floor(getblockchaininfo.blocks / coinConfig.difficultyAdjustmentBlockCount));
let blockHeights = [];
if (getblockchaininfo.blocks) {
for (let i = 0; i < 1; i++) {
blockHeights.push(getblockchaininfo.blocks - i);
}
} else if (global.activeBlockchain == "regtest") {
// hack: default regtest node returns getblockchaininfo.blocks=0, despite
// having a genesis block; hack this to display the genesis block
blockHeights.push(0);
}
promises.push(utils.timePromise("homepage.getBlockHeaderByHeight", async () => {
let h = coinConfig.difficultyAdjustmentBlockCount * res.locals.difficultyPeriod;
res.locals.difficultyPeriodFirstBlockHeader = await coreApi.getBlockHeaderByHeight(h);
}));
promises.push(utils.timePromise("homepage.getBlocksByHeight", async () => {
const latestBlocks = await coreApi.getBlocksByHeight(blockHeights);
res.locals.latestBlocks = latestBlocks;
}));
await utils.awaitPromises(promises);
let nextHalvingData = utils.nextHalvingEstimates(res.locals.difficultyPeriodFirstBlockHeader, res.locals.latestBlocks[0]);
// timeAgo = moment.duration(moment.utc(new Date()).diff(moment.utc(new Date())));
let timeAgo = moment.duration(moment.utc(nextHalvingData.nextHalvingDate).diff(moment.utc(new Date())));
let format = timeAgo.format();
let formatParts = format.split(",").map(x => x.trim());
formatParts = formatParts.map(x => { return x.startsWith("0 ") ? "" : x; }).filter(x => x.length > 0);
res.json({
nextHalvingIndex: nextHalvingData.nextHalvingIndex,
nextHalvingBlock: nextHalvingData.nextHalvingBlock,
nextHalvingSubsidy: coinConfig.blockRewardFunction(nextHalvingData.nextHalvingBlock, global.activeBlockchain),
blocksUntilNextHalving: nextHalvingData.blocksUntilNextHalving,
timeUntilNextHalving: formatParts.join(", "),
nextHalvingEstimatedDate: nextHalvingData.nextHalvingDate,
});
next();
} catch (e) {
utils.logError("013923hege3", e)
res.json({success:false});
next();
}
}));
@ -793,12 +864,6 @@ router.get("/mining/miner-summary", asyncHandler(async (req, res, next) => {
/// MEMPOOL
router.get("/mempool/count", function(req, res, next) {
coreApi.getMempoolInfo().then(function(info){
res.send(info.size.toString());
}).catch(next);
});
router.get("/mempool/summary", function(req, res, next) {
coreApi.getMempoolInfo().then(function(info){
res.json(info);
@ -840,30 +905,35 @@ router.get("/mempool/fees", function(req, res, next) {
/// PRICE
router.get("/price/:currency/sats", function(req, res, next) {
let result = 0;
let amount = 1.0;
let currency = req.params.currency.toLowerCase();
if (global.exchangeRates != null && global.exchangeRates[currency] != null) {
let satsRateData = utils.satoshisPerUnitOfLocalCurrency(currency);
result = satsRateData.amtRaw;
const supportedCurrencies = ["usd", "eur", "gbp", "xau"];
} else if (currency == "xau" && global.exchangeRates != null && global.goldExchangeRates != null) {
let dec = new Decimal(amount);
dec = dec.times(global.exchangeRates.usd).dividedBy(global.goldExchangeRates.usd);
let satCurrencyType = global.currencyTypes["sat"];
let one = new Decimal(1);
dec = one.dividedBy(dec);
dec = dec.times(satCurrencyType.multiplier);
result = dec.toFixed(0);
}
router.get("/price/sats", function(req, res, next) {
let result = {};
let amount = 1.0;
supportedCurrencies.forEach(currency => {
if (global.exchangeRates != null && global.exchangeRates[currency] != null) {
let satsRateData = utils.satoshisPerUnitOfLocalCurrency(currency);
result[currency] = satsRateData.amtRaw;
} else if (currency == "xau" && global.exchangeRates != null && global.goldExchangeRates != null) {
let dec = new Decimal(amount);
dec = dec.times(global.exchangeRates.usd).dividedBy(global.goldExchangeRates.usd);
let satCurrencyType = global.currencyTypes["sat"];
let one = new Decimal(1);
dec = one.dividedBy(dec);
dec = dec.times(satCurrencyType.multiplier);
result[currency] = dec.toFixed(0);
}
});
res.send(result.toString());
res.json(result);
next();
});
router.get("/price/:currency/marketcap", function(req, res, next) {
router.get("/price/marketcap", function(req, res, next) {
let result = 0;
coreApi.getBlockchainInfo().then(function(getblockchaininfo){
@ -871,58 +941,36 @@ router.get("/price/:currency/marketcap", function(req, res, next) {
let price = 0;
let amount = 1.0;
let currency = req.params.currency.toLowerCase();
if (global.exchangeRates != null && global.exchangeRates[currency] != null) {
let formatData = utils.formatExchangedCurrency(amount, currency);
price = parseFloat(formatData.valRaw).toFixed(2);
let result = {};
supportedCurrencies.forEach(currency => {
if (global.exchangeRates != null && global.exchangeRates[currency] != null) {
let formatData = utils.formatExchangedCurrency(amount, currency);
price = parseFloat(formatData.valRaw).toFixed(2);
} else if (currency == "xau" && global.exchangeRates != null && global.goldExchangeRates != null) {
let dec = new Decimal(amount);
dec = dec.times(global.exchangeRates.usd).dividedBy(global.goldExchangeRates.usd);
let exchangedAmt = parseFloat(Math.round(dec * 100) / 100).toFixed(2);
price = exchangedAmt;
}
result[currency] = estimatedSupply * price;
});
res.json(result);
} else if (currency == "xau" && global.exchangeRates != null && global.goldExchangeRates != null) {
let dec = new Decimal(amount);
dec = dec.times(global.exchangeRates.usd).dividedBy(global.goldExchangeRates.usd);
let exchangedAmt = parseFloat(Math.round(dec * 100) / 100).toFixed(2);
price = exchangedAmt;
}
result = estimatedSupply * price;
res.send(result.toFixed(2).toString());
next();
}).catch(next);
});
router.get("/price/:currency", function(req, res, next) {
let result = 0;
let amount = 1.0;
let currency = req.params.currency.toLowerCase();
let format = (req.query.format == "true");
if (global.exchangeRates != null && global.exchangeRates[currency] != null) {
let formatData = utils.formatExchangedCurrency(amount, currency);
if (format) {
result = formatData.val;
} else {
result = formatData.valRaw;
}
} else if (currency == "xau" && global.exchangeRates != null && global.goldExchangeRates != null) {
let dec = new Decimal(amount);
dec = dec.times(global.exchangeRates.usd).dividedBy(global.goldExchangeRates.usd);
let exchangedAmt = parseFloat(Math.round(dec * 100) / 100).toFixed(2);
result = utils.addThousandsSeparators(exchangedAmt);
}
res.send(result.toString());
next();
});
router.get("/price", function(req, res, next) {
let amount = 1.0;
let result = {};
let format = (req.query.format == "true");
["usd", "eur", "gbp", "xau"].forEach(currency => {
supportedCurrencies.forEach(currency => {
if (global.exchangeRates != null && global.exchangeRates[currency] != null) {
let formatData = utils.formatExchangedCurrency(amount, currency);

View file

@ -6,9 +6,9 @@ block headContent
block content
+pageTitle("API Docs")
div.fs-4.mt-n3.mb-3 Current Version:
a.ms-2(href="./api/version", title="View version API call: /api/version", data-bs-toggle="tooltip", target="_blank") v#{apiDocs.version}
a.ms-2(href=`.${apiBaseUrl}/version`, title=`View version API call: ${apiBaseUrl}/version`, data-bs-toggle="tooltip", target="_blank") v#{apiDocs.version}
small.ms-2 (
a(href="./api/changelog") changelog
a(href=`.${apiBaseUrl}/changelog`) changelog
| )
if (false)
@ -19,7 +19,7 @@ block content
+dismissableInfoAlert("apiDocsNoteDismissed", "About the API...")
h6.mb-2 About the API
| The API documented below is made available by btc-rpc-explorer. The actions are organized by category. From this documentation you can directly click on an action's link to see the output format in your browser.
| The API documented below is made available by btc-rpc-explorer. All actions return JSON content. The actions are organized by category. From this documentation you can directly click on an action's link to see the output format in your browser.
each cat, catIndex in categories
@ -32,13 +32,9 @@ block content
.row.p-2
.col-md-3
a(href=(item.testUrl ? `.${item.testUrl}` : `.${item.url}`), target="_blank") #{item.url}
a(href=(item.testUrl ? `.${apiBaseUrl}${item.testUrl}` : `.${apiBaseUrl}${item.url}`), target="_blank") #{apiBaseUrl}#{item.url}
.col-md-1
span(title=`Return type: ${item.returnType}`, data-bs-toggle="tooltip")
+lightBadge(item.returnType)
.col-md-8
.col-md-9
div #{item.desc}