From f4d2ec9f203e8c44f8179d87f20e0d67d6dc8f6a Mon Sep 17 00:00:00 2001 From: Thomas Droxler Date: Wed, 1 May 2024 16:46:54 +0200 Subject: [PATCH 1/3] Refactor config with `services` that can be enabled --- app/src/main/resources/application.conf | 62 +++++++++++++------ .../org/alephium/explorer/SyncServices.scala | 10 +-- .../explorer/config/ExplorerConfig.scala | 57 ++++++++++++----- 3 files changed, 90 insertions(+), 39 deletions(-) diff --git a/app/src/main/resources/application.conf b/app/src/main/resources/application.conf index 26fc934b2..d16f18204 100644 --- a/app/src/main/resources/application.conf +++ b/app/src/main/resources/application.conf @@ -14,25 +14,49 @@ explorer { boot-mode = ReadWrite boot-mode = ${?EXPLORER_BOOT_MODE} - # Sync interval for BlockFlowSyncService & MempoolSyncService - sync-period = 5 seconds - sync-period = ${?EXPLORER_SYNC_PERIOD} - - # Schedule time for TokenSupplyService - token-supply-service-schedule-time = "02:00" - token-supply-service-schedule-time = ${?EXPLORER_TOKEN_SUPPLY_SERVICE_SCHEDULE_TIME} - - # Sync interval for HashRateService - hash-rate-service-sync-period = 1 hours - hash-rate-service-sync-period = ${?EXPLORER_HASH_RATE_SERVICE_SYNC_PERIOD} - - # Sync interval for FinalizerService - finalizer-service-sync-period = 10 minutes - finalizer-service-sync-period = ${?EXPLORER_FINALIZER_SERVICE_SYNC_PERIOD} - - # Sync interval for TransactionHistoryService - transaction-history-service-sync-period = 15 minutes - transaction-history-service-sync-period = ${?EXPLORER_TRANSACTION_HISTORY_SERVICE_SYNC_PERIOD} + services { + blockflow-sync = { + # BlockFlowSync Service is always enabled + # Sync interval for BlockFlowSyncService + sync-period = 5 seconds + sync-period = ${?EXPLORER_SYNC_PERIOD} + } + + mempool-sync = { + enable = true, + # Sync interval for MempoolSyncService + sync-period = 5 seconds + sync-period = ${?EXPLORER_SYNC_PERIOD} + } + + token-supply = { + enable = true, + # Schedule time for TokenSupplyService + schedule-time = "02:00" + schedule-time = ${?EXPLORER_TOKEN_SUPPLY_SERVICE_SCHEDULE_TIME} + } + + hashrate = { + enable = true, + # Sync interval for HashRateService + sync-period = 1 hours + sync-period = ${?EXPLORER_HASH_RATE_SERVICE_SYNC_PERIOD} + } + + tx-history = { + enable = true, + # Sync interval for TransactionHistoryService + sync-period = 15 minutes + sync-period = ${?EXPLORER_TRANSACTION_HISTORY_SERVICE_SYNC_PERIOD} + } + + finalizer = { + # FinalizerService is always enabled + # Sync interval for FinalizerService + sync-period = 10 minutes + sync-period = ${?EXPLORER_FINALIZER_SERVICE_SYNC_PERIOD} + } + } # Cache reloading intervals for BlockCache cache-row-count-reload-period = 10 seconds diff --git a/app/src/main/scala/org/alephium/explorer/SyncServices.scala b/app/src/main/scala/org/alephium/explorer/SyncServices.scala index 0203cba90..62ecd671e 100644 --- a/app/src/main/scala/org/alephium/explorer/SyncServices.scala +++ b/app/src/main/scala/org/alephium/explorer/SyncServices.scala @@ -62,11 +62,11 @@ object SyncServices extends StrictLogging { ) flatMap { peers => startSyncServices( peers = peers, - syncPeriod = config.syncPeriod, - tokenSupplyServiceScheduleTime = config.tokenSupplyServiceScheduleTime, - hashRateServiceSyncPeriod = config.hashRateServiceSyncPeriod, - finalizerServiceSyncPeriod = config.finalizerServiceSyncPeriod, - transactionHistoryServiceSyncPeriod = config.transactionHistoryServiceSyncPeriod + syncPeriod = config.services.blockflowSync.syncPeriod, + tokenSupplyServiceScheduleTime = config.services.tokenSupply.scheduleTime, + hashRateServiceSyncPeriod = config.services.hashrate.syncPeriod, + finalizerServiceSyncPeriod = config.services.finalizer.syncPeriod, + transactionHistoryServiceSyncPeriod = config.services.txHistory.syncPeriod ) } } diff --git a/app/src/main/scala/org/alephium/explorer/config/ExplorerConfig.scala b/app/src/main/scala/org/alephium/explorer/config/ExplorerConfig.scala index 153b40805..64d1623db 100644 --- a/app/src/main/scala/org/alephium/explorer/config/ExplorerConfig.scala +++ b/app/src/main/scala/org/alephium/explorer/config/ExplorerConfig.scala @@ -150,11 +150,7 @@ object ExplorerConfig { host, port, explorer.bootMode, - explorer.syncPeriod, - explorer.tokenSupplyServiceScheduleTime, - explorer.hashRateServiceSyncPeriod, - explorer.finalizerServiceSyncPeriod, - explorer.transactionHistoryServiceSyncPeriod, + explorer.services, explorer.cacheRowCountReloadPeriod, explorer.cacheBlockTimesReloadPeriod, explorer.cacheLatestBlocksReloadPeriod, @@ -197,15 +193,50 @@ object ExplorerConfig { marketChartDays: Int ) + final case class Services( + blockflowSync: Services.BlockflowSync, + mempoolSync: Services.MempoolSync, + tokenSupply: Services.TokenSupply, + hashrate: Services.Hashrate, + txHistory: Services.TxHistory, + finalizer: Services.Finalizer + ) + + object Services { + final case class BlockflowSync( + syncPeriod: FiniteDuration + ) + + final case class MempoolSync( + enable: Boolean, + syncPeriod: FiniteDuration + ) + + final case class TokenSupply( + enable: Boolean, + scheduleTime: LocalTime + ) + + final case class Hashrate( + enable: Boolean, + syncPeriod: FiniteDuration + ) + + final case class TxHistory( + enable: Boolean, + syncPeriod: FiniteDuration + ) + + final case class Finalizer( + syncPeriod: FiniteDuration + ) + } + final private case class Explorer( host: String, port: Int, bootMode: BootMode, - syncPeriod: FiniteDuration, - tokenSupplyServiceScheduleTime: LocalTime, - hashRateServiceSyncPeriod: FiniteDuration, - finalizerServiceSyncPeriod: FiniteDuration, - transactionHistoryServiceSyncPeriod: FiniteDuration, + services: Services, cacheRowCountReloadPeriod: FiniteDuration, cacheBlockTimesReloadPeriod: FiniteDuration, cacheLatestBlocksReloadPeriod: FiniteDuration, @@ -230,11 +261,7 @@ final case class ExplorerConfig private ( host: String, port: Int, bootMode: BootMode, - syncPeriod: FiniteDuration, - tokenSupplyServiceScheduleTime: LocalTime, - hashRateServiceSyncPeriod: FiniteDuration, - finalizerServiceSyncPeriod: FiniteDuration, - transactionHistoryServiceSyncPeriod: FiniteDuration, + services: ExplorerConfig.Services, cacheRowCountReloadPeriod: FiniteDuration, cacheBlockTimesReloadPeriod: FiniteDuration, cacheLatestBlocksReloadPeriod: FiniteDuration, From e9ab6dc40a988d3877927177cc8721ec3c0fa403 Mon Sep 17 00:00:00 2001 From: Thomas Droxler Date: Wed, 1 May 2024 17:00:31 +0200 Subject: [PATCH 2/3] Start services according to config --- app/src/main/resources/application.conf | 4 ++ .../org/alephium/explorer/SyncServices.scala | 39 +++++++++---------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/src/main/resources/application.conf b/app/src/main/resources/application.conf index d16f18204..214dbdb9d 100644 --- a/app/src/main/resources/application.conf +++ b/app/src/main/resources/application.conf @@ -24,6 +24,7 @@ explorer { mempool-sync = { enable = true, + enable = ${?EXPLORER_MEMPOOL_SYNC_ENABLE} # Sync interval for MempoolSyncService sync-period = 5 seconds sync-period = ${?EXPLORER_SYNC_PERIOD} @@ -31,12 +32,14 @@ explorer { token-supply = { enable = true, + enable = ${?EXPLORER_TOKEN_SUPPLY_ENABLE} # Schedule time for TokenSupplyService schedule-time = "02:00" schedule-time = ${?EXPLORER_TOKEN_SUPPLY_SERVICE_SCHEDULE_TIME} } hashrate = { + enable = ${?EXPLORER_HASHRATE_ENABLE} enable = true, # Sync interval for HashRateService sync-period = 1 hours @@ -44,6 +47,7 @@ explorer { } tx-history = { + enable = ${?EXPLORER_TX_HISTORY_ENABLE} enable = true, # Sync interval for TransactionHistoryService sync-period = 15 minutes diff --git a/app/src/main/scala/org/alephium/explorer/SyncServices.scala b/app/src/main/scala/org/alephium/explorer/SyncServices.scala index 62ecd671e..e4d76e614 100644 --- a/app/src/main/scala/org/alephium/explorer/SyncServices.scala +++ b/app/src/main/scala/org/alephium/explorer/SyncServices.scala @@ -16,11 +16,8 @@ package org.alephium.explorer -import java.time.LocalTime - import scala.collection.immutable.ArraySeq import scala.concurrent.{ExecutionContext, Future} -import scala.concurrent.duration._ import scala.util.{Failure, Success, Try} import com.typesafe.scalalogging.StrictLogging @@ -62,11 +59,7 @@ object SyncServices extends StrictLogging { ) flatMap { peers => startSyncServices( peers = peers, - syncPeriod = config.services.blockflowSync.syncPeriod, - tokenSupplyServiceScheduleTime = config.services.tokenSupply.scheduleTime, - hashRateServiceSyncPeriod = config.services.hashrate.syncPeriod, - finalizerServiceSyncPeriod = config.services.finalizer.syncPeriod, - transactionHistoryServiceSyncPeriod = config.services.txHistory.syncPeriod + config = config.services ) } } @@ -75,11 +68,7 @@ object SyncServices extends StrictLogging { // scalastyle:off def startSyncServices( peers: ArraySeq[Uri], - syncPeriod: FiniteDuration, - tokenSupplyServiceScheduleTime: LocalTime, - hashRateServiceSyncPeriod: FiniteDuration, - finalizerServiceSyncPeriod: FiniteDuration, - transactionHistoryServiceSyncPeriod: FiniteDuration + config: ExplorerConfig.Services )(implicit scheduler: Scheduler, ec: ExecutionContext, @@ -93,13 +82,23 @@ object SyncServices extends StrictLogging { Future .sequence( ArraySeq( - BlockFlowSyncService.start(peers, syncPeriod), - MempoolSyncService.start(peers, syncPeriod), - TokenSupplyService.start(tokenSupplyServiceScheduleTime), - HashrateService.start(hashRateServiceSyncPeriod), - FinalizerService.start(finalizerServiceSyncPeriod), - TransactionHistoryService.start(transactionHistoryServiceSyncPeriod) - ) + BlockFlowSyncService.start(peers, config.blockflowSync.syncPeriod), + FinalizerService.start(config.finalizer.syncPeriod) + ) ++ + ArraySeq( + Option.when(config.mempoolSync.enable)( + MempoolSyncService.start(peers, config.mempoolSync.syncPeriod) + ), + Option.when(config.tokenSupply.enable)( + TokenSupplyService.start(config.tokenSupply.scheduleTime) + ), + Option.when(config.hashrate.enable)( + HashrateService.start(config.hashrate.syncPeriod) + ), + Option.when(config.txHistory.enable)( + TransactionHistoryService.start(config.txHistory.syncPeriod) + ) + ).flatten ) .onComplete { case Failure(error) => From aa386003a7d548b5b2919549e9dd0cf396267cd2 Mon Sep 17 00:00:00 2001 From: Thomas Droxler Date: Fri, 3 May 2024 09:45:05 +0200 Subject: [PATCH 3/3] Disable endpoints when corresponding service is disabled Resolves: #534 --- .../resources/explorer-backend-openapi.json | 1951 ++++++++--------- .../org/alephium/explorer/AppServer.scala | 13 +- .../org/alephium/explorer/ExplorerState.scala | 3 +- .../explorer/docs/Documentation.scala | 173 +- .../alephium/explorer/web/ChartsServer.scala | 60 +- .../explorer/web/DocumentationServer.scala | 6 +- .../alephium/explorer/web/InfosServer.scala | 92 +- .../alephium/explorer/web/MempoolServer.scala | 20 +- .../alephium/explorer/ConfigDefaults.scala | 27 + .../explorer/web/ChartsServerSpec.scala | 3 +- .../explorer/web/InfosServerSpec.scala | 2 +- .../explorer/web/MempoolServerSpec.scala | 4 +- .../org/alephium/tools/OpenApiUpdate.scala | 8 + 13 files changed, 1209 insertions(+), 1153 deletions(-) diff --git a/app/src/main/resources/explorer-backend-openapi.json b/app/src/main/resources/explorer-backend-openapi.json index e5bb69d57..57bd6824c 100644 --- a/app/src/main/resources/explorer-backend-openapi.json +++ b/app/src/main/resources/explorer-backend-openapi.json @@ -681,6 +681,255 @@ } } }, + "/mempool/transactions": { + "get": { + "tags": [ + "Mempool" + ], + "description": "list mempool transactions", + "operationId": "getMempoolTransactions", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "limit", + "in": "query", + "description": "Number of items per page", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MempoolTransaction" + } + }, + "example": [ + { + "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "chainFrom": 1, + "chainTo": 2, + "inputs": [ + { + "outputRef": { + "hint": 23412, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", + "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "attoAlphAmount": "2", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "outputs": [ + { + "type": "AssetOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ], + "lockTime": 1611041396892, + "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + { + "type": "ContractOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "gasAmount": 20000, + "gasPrice": "100000000000", + "lastSeen": 1611041396892 + }, + { + "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "chainFrom": 1, + "chainTo": 2, + "inputs": [ + { + "outputRef": { + "hint": 23412, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", + "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "attoAlphAmount": "2", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "outputs": [ + { + "type": "AssetOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ], + "lockTime": 1611041396892, + "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + { + "type": "ContractOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "gasAmount": 20000, + "gasPrice": "100000000000", + "lastSeen": 1611041396892 + } + ] + } + } + }, + "400": { + "description": "BadRequest", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequest" + }, + "example": { + "detail": "Something bad in the request" + } + } + } + }, + "401": { + "description": "Unauthorized", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Unauthorized" + }, + "example": { + "detail": "You shall not pass" + } + } + } + }, + "404": { + "description": "NotFound", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFound" + }, + "example": { + "resource": "wallet-name", + "detail": "wallet-name not found" + } + } + } + }, + "500": { + "description": "InternalServerError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InternalServerError" + }, + "example": { + "detail": "Ouch" + } + } + } + }, + "503": { + "description": "ServiceUnavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailable" + }, + "example": { + "detail": "Self clique unsynced" + } + } + } + } + } + } + }, "/addresses/{address}": { "get": { "tags": [ @@ -3360,35 +3609,13 @@ } } }, - "/mempool/transactions": { + "/infos/average-block-times": { "get": { "tags": [ - "Mempool" - ], - "description": "list mempool transactions", - "operationId": "getMempoolTransactions", - "parameters": [ - { - "name": "page", - "in": "query", - "description": "Page number", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "limit", - "in": "query", - "description": "Number of items per page", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - } + "Infos" ], + "description": "Get the average block time for each chain", + "operationId": "getInfosAverage-block-times", "responses": { "200": { "content": { @@ -3396,145 +3623,21 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MempoolTransaction" + "$ref": "#/components/schemas/PerChainDuration" } }, "example": [ { - "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", "chainFrom": 1, "chainTo": 2, - "inputs": [ - { - "outputRef": { - "hint": 23412, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", - "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "attoAlphAmount": "2", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "outputs": [ - { - "type": "AssetOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ], - "lockTime": 1611041396892, - "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - { - "type": "ContractOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "gasAmount": 20000, - "gasPrice": "100000000000", - "lastSeen": 1611041396892 + "duration": 60, + "value": 60 }, { - "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", "chainFrom": 1, "chainTo": 2, - "inputs": [ - { - "outputRef": { - "hint": 23412, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", - "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "attoAlphAmount": "2", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "outputs": [ - { - "type": "AssetOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ], - "lockTime": 1611041396892, - "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - { - "type": "ContractOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "gasAmount": 20000, - "gasPrice": "100000000000", - "lastSeen": 1611041396892 + "duration": 60, + "value": 60 } ] } @@ -3609,13 +3712,13 @@ } } }, - "/tokens": { + "/infos/supply": { "get": { "tags": [ - "Tokens" + "Infos" ], - "description": "List token information", - "operationId": "getTokens", + "description": "Get token supply list", + "operationId": "getInfosSupply", "parameters": [ { "name": "page", @@ -3636,25 +3739,6 @@ "type": "integer", "format": "int32" } - }, - { - "name": "interface-id", - "in": "query", - "description": "fungible, non-fungible, non-standard or any interface id in hex-string format, e.g: 0001", - "required": false, - "schema": { - "format": "string", - "oneOf": [ - { - "$ref": "#/components/schemas/TokenStdInterfaceId" - }, - { - "type": "string", - "description": "Raw interface id, e.g. 0001", - "format": "hex-string" - } - ] - } } ], "responses": { @@ -3664,13 +3748,25 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TokenInfo" + "$ref": "#/components/schemas/TokenSupply" } }, "example": [ { - "token": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", - "stdInterfaceId": "fungible" + "timestamp": 1611041396892, + "total": "1000000000000000000", + "circulating": "500000000000000000", + "reserved": "10", + "locked": "10", + "maximum": "1000000000000000000000000000" + }, + { + "timestamp": 1611041396892, + "total": "1000000000000000000", + "circulating": "500000000000000000", + "reserved": "10", + "locked": "10", + "maximum": "1000000000000000000000000000" } ] } @@ -3743,48 +3839,22 @@ } } } - }, - "post": { + } + }, + "/infos/supply/circulating-alph": { + "get": { "tags": [ - "Tokens" + "Infos" ], - "description": "List given tokens information", - "operationId": "postTokens", - "requestBody": { - "description": "List of token ids, max items: 80", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "32-byte-hash" - }, - "maxItems": 80 - }, - "example": [ - "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2" - ] - } - }, - "required": false - }, + "description": "Get the ALPH circulating supply", + "operationId": "getInfosSupplyCirculating-alph", "responses": { "200": { "content": { - "application/json": { + "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TokenInfo" - } - }, - "example": [ - { - "token": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", - "stdInterfaceId": "fungible" - } - ] + "type": "number" + } } } }, @@ -3857,194 +3927,20 @@ } } }, - "/tokens/{token_id}/transactions": { + "/infos/supply/total-alph": { "get": { "tags": [ - "Tokens" - ], - "description": "List token transactions", - "operationId": "getTokensToken_idTransactions", - "parameters": [ - { - "name": "token_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "32-byte-hash" - } - }, - { - "name": "page", - "in": "query", - "description": "Page number", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "limit", - "in": "query", - "description": "Number of items per page", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - } + "Infos" ], + "description": "Get the ALPH total supply", + "operationId": "getInfosSupplyTotal-alph", "responses": { "200": { "content": { - "application/json": { + "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Transaction" - } - }, - "example": [ - { - "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "timestamp": 1611041396892, - "inputs": [ - { - "outputRef": { - "hint": 23412, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", - "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "attoAlphAmount": "2", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "outputs": [ - { - "type": "AssetOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ], - "lockTime": 1611041396892, - "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - { - "type": "ContractOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "gasAmount": 20000, - "gasPrice": "100000000000", - "scriptExecutionOk": true, - "coinbase": false - }, - { - "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "timestamp": 1611041396892, - "inputs": [ - { - "outputRef": { - "hint": 23412, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", - "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "attoAlphAmount": "2", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "outputs": [ - { - "type": "AssetOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ], - "lockTime": 1611041396892, - "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" - }, - { - "type": "ContractOutput", - "hint": 1, - "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", - "attoAlphAmount": "2", - "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "tokens": [ - { - "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", - "amount": "42000000000000000000" - }, - { - "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", - "amount": "1000000000000000000000" - } - ] - } - ], - "gasAmount": 20000, - "gasPrice": "100000000000", - "scriptExecutionOk": true, - "coinbase": false - } - ] + "type": "number" + } } } }, @@ -4117,58 +4013,20 @@ } } }, - "/tokens/{token_id}/addresses": { + "/infos/supply/reserved-alph": { "get": { "tags": [ - "Tokens" - ], - "description": "List token addresses", - "operationId": "getTokensToken_idAddresses", - "parameters": [ - { - "name": "token_id", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "32-byte-hash" - } - }, - { - "name": "page", - "in": "query", - "description": "Page number", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "limit", - "in": "query", - "description": "Number of items per page", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - } + "Infos" ], + "description": "Get the ALPH reserved supply", + "operationId": "getInfosSupplyReserved-alph", "responses": { "200": { "content": { - "application/json": { + "text/plain": { "schema": { - "type": "array", - "items": { - "type": "string", - "format": "address" - } - }, - "example": [ - "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y" - ] + "type": "number" + } } } }, @@ -4241,63 +4099,20 @@ } } }, - "/infos/supply": { + "/infos/supply/locked-alph": { "get": { "tags": [ "Infos" ], - "description": "Get token supply list", - "operationId": "getInfosSupply", - "parameters": [ - { - "name": "page", - "in": "query", - "description": "Page number", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - }, - { - "name": "limit", - "in": "query", - "description": "Number of items per page", - "required": false, - "schema": { - "type": "integer", - "format": "int32" - } - } - ], + "description": "Get the ALPH locked supply", + "operationId": "getInfosSupplyLocked-alph", "responses": { "200": { "content": { - "application/json": { + "text/plain": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TokenSupply" - } - }, - "example": [ - { - "timestamp": 1611041396892, - "total": "1000000000000000000", - "circulating": "500000000000000000", - "reserved": "10", - "locked": "10", - "maximum": "1000000000000000000000000000" - }, - { - "timestamp": 1611041396892, - "total": "1000000000000000000", - "circulating": "500000000000000000", - "reserved": "10", - "locked": "10", - "maximum": "1000000000000000000000000000" - } - ] + "type": "number" + } } } }, @@ -4370,148 +4185,54 @@ } } }, - "/tokens/fungible-metadata": { - "post": { + "/tokens": { + "get": { "tags": [ "Tokens" ], - "description": "Return metadata for the given fungible tokens, if metadata doesn't exist or token isn't a fungible, it won't be in the output list", - "operationId": "postTokensFungible-metadata", - "requestBody": { - "description": "List of token ids, max items: 80", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "32-byte-hash" - }, - "maxItems": 80 - }, - "example": [ - "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2" - ] - } - }, - "required": false - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FungibleTokenMetadata" - } - }, - "example": [ - { - "id": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", - "symbol": "TK", - "name": "Token", - "decimals": "1" - } - ] - } - } - }, - "400": { - "description": "BadRequest", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BadRequest" - }, - "example": { - "detail": "Something bad in the request" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Unauthorized" - }, - "example": { - "detail": "You shall not pass" - } - } - } - }, - "404": { - "description": "NotFound", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotFound" - }, - "example": { - "resource": "wallet-name", - "detail": "wallet-name not found" - } - } + "description": "List token information", + "operationId": "getTokens", + "parameters": [ + { + "name": "page", + "in": "query", + "description": "Page number", + "required": false, + "schema": { + "type": "integer", + "format": "int32" } }, - "500": { - "description": "InternalServerError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InternalServerError" - }, - "example": { - "detail": "Ouch" - } - } + { + "name": "limit", + "in": "query", + "description": "Number of items per page", + "required": false, + "schema": { + "type": "integer", + "format": "int32" } }, - "503": { - "description": "ServiceUnavailable", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceUnavailable" + { + "name": "interface-id", + "in": "query", + "description": "fungible, non-fungible, non-standard or any interface id in hex-string format, e.g: 0001", + "required": false, + "schema": { + "format": "string", + "oneOf": [ + { + "$ref": "#/components/schemas/TokenStdInterfaceId" }, - "example": { - "detail": "Self clique unsynced" + { + "type": "string", + "description": "Raw interface id, e.g. 0001", + "format": "hex-string" } - } + ] } } - } - } - }, - "/tokens/nft-metadata": { - "post": { - "tags": [ - "Tokens" ], - "description": "Return metadata for the given nft tokens, if metadata doesn't exist or token isn't a nft, it won't be in the output list", - "operationId": "postTokensNft-metadata", - "requestBody": { - "description": "List of token ids, max items: 80", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "string", - "format": "32-byte-hash" - }, - "maxItems": 80 - }, - "example": [ - "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2" - ] - } - }, - "required": false - }, "responses": { "200": { "content": { @@ -4519,15 +4240,13 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NFTMetadata" + "$ref": "#/components/schemas/TokenInfo" } }, "example": [ { - "id": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", - "tokenUri": "token://uri", - "collectionId": "ac92820d7b37ab2b14eca20839a9c1ad5379e671c687b37a416a2754ea9fc412", - "nftIndex": "1" + "token": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", + "stdInterfaceId": "fungible" } ] } @@ -4600,29 +4319,27 @@ } } } - } - }, - "/tokens/nft-collection-metadata": { + }, "post": { "tags": [ "Tokens" ], - "description": "Return metadata for the given nft collection addresses, if metadata doesn't exist or address isn't a nft collection, it won't be in the output list", - "operationId": "postTokensNft-collection-metadata", + "description": "List given tokens information", + "operationId": "postTokens", "requestBody": { - "description": "List of addresses, max items: 80", + "description": "List of token ids, max items: 80", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string", - "format": "address" + "format": "32-byte-hash" }, "maxItems": 80 }, "example": [ - "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y" + "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2" ] } }, @@ -4635,13 +4352,13 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/NFTCollectionMetadata" + "$ref": "#/components/schemas/TokenInfo" } }, "example": [ { - "address": "26JbotDoUoNzwVACg1YLaNTZKAJvoBDWiXJMnokZxCzvd", - "collectionUri": "collection://uri" + "token": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", + "stdInterfaceId": "fungible" } ] } @@ -4716,20 +4433,194 @@ } } }, - "/infos/supply/total-alph": { + "/tokens/{token_id}/transactions": { "get": { "tags": [ - "Infos" + "Tokens" + ], + "description": "List token transactions", + "operationId": "getTokensToken_idTransactions", + "parameters": [ + { + "name": "token_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "32-byte-hash" + } + }, + { + "name": "page", + "in": "query", + "description": "Page number", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "limit", + "in": "query", + "description": "Number of items per page", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } ], - "description": "Get the ALPH total supply", - "operationId": "getInfosSupplyTotal-alph", "responses": { "200": { "content": { - "text/plain": { + "application/json": { "schema": { - "type": "number" - } + "type": "array", + "items": { + "$ref": "#/components/schemas/Transaction" + } + }, + "example": [ + { + "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "timestamp": 1611041396892, + "inputs": [ + { + "outputRef": { + "hint": 23412, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", + "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "attoAlphAmount": "2", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "outputs": [ + { + "type": "AssetOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ], + "lockTime": 1611041396892, + "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + { + "type": "ContractOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "gasAmount": 20000, + "gasPrice": "100000000000", + "scriptExecutionOk": true, + "coinbase": false + }, + { + "hash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "timestamp": 1611041396892, + "inputs": [ + { + "outputRef": { + "hint": 23412, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + "unlockScript": "d1b70d2226308b46da297486adb6b4f1a8c1842cb159ac5ec04f384fe2d6f5da28", + "txHashRef": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "attoAlphAmount": "2", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "outputs": [ + { + "type": "AssetOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ], + "lockTime": 1611041396892, + "message": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef" + }, + { + "type": "ContractOutput", + "hint": 1, + "key": "798e9e137aec7c2d59d9655b4ffa640f301f628bf7c365083bb255f6aa5f89ef", + "attoAlphAmount": "2", + "address": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "tokens": [ + { + "id": "2d11fd6c12435ffb07aaed4d190a505b621b927a5f6e51b61ce0ebe186397bdd", + "amount": "42000000000000000000" + }, + { + "id": "bd165d20bd063c7a023d22232a1e75bf46e904067f92b49323fe89fa0fd586bf", + "amount": "1000000000000000000000" + } + ] + } + ], + "gasAmount": 20000, + "gasPrice": "100000000000", + "scriptExecutionOk": true, + "coinbase": false + } + ] } } }, @@ -4802,106 +4693,58 @@ } } }, - "/infos/supply/circulating-alph": { + "/tokens/{token_id}/addresses": { "get": { "tags": [ - "Infos" + "Tokens" ], - "description": "Get the ALPH circulating supply", - "operationId": "getInfosSupplyCirculating-alph", - "responses": { - "200": { - "content": { - "text/plain": { - "schema": { - "type": "number" - } - } - } - }, - "400": { - "description": "BadRequest", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BadRequest" - }, - "example": { - "detail": "Something bad in the request" - } - } - } - }, - "401": { - "description": "Unauthorized", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Unauthorized" - }, - "example": { - "detail": "You shall not pass" - } - } - } - }, - "404": { - "description": "NotFound", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotFound" - }, - "example": { - "resource": "wallet-name", - "detail": "wallet-name not found" - } - } + "description": "List token addresses", + "operationId": "getTokensToken_idAddresses", + "parameters": [ + { + "name": "token_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "32-byte-hash" } }, - "500": { - "description": "InternalServerError", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InternalServerError" - }, - "example": { - "detail": "Ouch" - } - } + { + "name": "page", + "in": "query", + "description": "Page number", + "required": false, + "schema": { + "type": "integer", + "format": "int32" } }, - "503": { - "description": "ServiceUnavailable", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceUnavailable" - }, - "example": { - "detail": "Self clique unsynced" - } - } + { + "name": "limit", + "in": "query", + "description": "Number of items per page", + "required": false, + "schema": { + "type": "integer", + "format": "int32" } } - } - } - }, - "/infos/supply/reserved-alph": { - "get": { - "tags": [ - "Infos" ], - "description": "Get the ALPH reserved supply", - "operationId": "getInfosSupplyReserved-alph", "responses": { "200": { "content": { - "text/plain": { + "application/json": { "schema": { - "type": "number" - } + "type": "array", + "items": { + "type": "string", + "format": "address" + } + }, + "example": [ + "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y" + ] } } }, @@ -4974,20 +4817,50 @@ } } }, - "/infos/supply/locked-alph": { - "get": { + "/tokens/fungible-metadata": { + "post": { "tags": [ - "Infos" + "Tokens" ], - "description": "Get the ALPH locked supply", - "operationId": "getInfosSupplyLocked-alph", + "description": "Return metadata for the given fungible tokens, if metadata doesn't exist or token isn't a fungible, it won't be in the output list", + "operationId": "postTokensFungible-metadata", + "requestBody": { + "description": "List of token ids, max items: 80", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "32-byte-hash" + }, + "maxItems": 80 + }, + "example": [ + "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2" + ] + } + }, + "required": false + }, "responses": { "200": { "content": { - "text/plain": { + "application/json": { "schema": { - "type": "number" - } + "type": "array", + "items": { + "$ref": "#/components/schemas/FungibleTokenMetadata" + } + }, + "example": [ + { + "id": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", + "symbol": "TK", + "name": "Token", + "decimals": "1" + } + ] } } }, @@ -5060,21 +4933,50 @@ } } }, - "/infos/total-transactions": { - "get": { + "/tokens/nft-metadata": { + "post": { "tags": [ - "Infos" + "Tokens" ], - "description": "Get the total number of transactions", - "operationId": "getInfosTotal-transactions", + "description": "Return metadata for the given nft tokens, if metadata doesn't exist or token isn't a nft, it won't be in the output list", + "operationId": "postTokensNft-metadata", + "requestBody": { + "description": "List of token ids, max items: 80", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "32-byte-hash" + }, + "maxItems": 80 + }, + "example": [ + "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2" + ] + } + }, + "required": false + }, "responses": { "200": { "content": { - "text/plain": { + "application/json": { "schema": { - "type": "integer", - "format": "int32" - } + "type": "array", + "items": { + "$ref": "#/components/schemas/NFTMetadata" + } + }, + "example": [ + { + "id": "7b3efc0a1c4bc54e7398811f7e474e461a9c00cbc951f33b886a5f207c820ef2", + "tokenUri": "token://uri", + "collectionId": "ac92820d7b37ab2b14eca20839a9c1ad5379e671c687b37a416a2754ea9fc412", + "nftIndex": "1" + } + ] } } }, @@ -5147,13 +5049,32 @@ } } }, - "/infos/average-block-times": { - "get": { + "/tokens/nft-collection-metadata": { + "post": { "tags": [ - "Infos" + "Tokens" ], - "description": "Get the average block time for each chain", - "operationId": "getInfosAverage-block-times", + "description": "Return metadata for the given nft collection addresses, if metadata doesn't exist or address isn't a nft collection, it won't be in the output list", + "operationId": "postTokensNft-collection-metadata", + "requestBody": { + "description": "List of addresses, max items: 80", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string", + "format": "address" + }, + "maxItems": 80 + }, + "example": [ + "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y" + ] + } + }, + "required": false + }, "responses": { "200": { "content": { @@ -5161,21 +5082,13 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PerChainDuration" + "$ref": "#/components/schemas/NFTCollectionMetadata" } }, "example": [ { - "chainFrom": 1, - "chainTo": 2, - "duration": 60, - "value": 60 - }, - { - "chainFrom": 1, - "chainTo": 2, - "duration": 60, - "value": 60 + "address": "26JbotDoUoNzwVACg1YLaNTZKAJvoBDWiXJMnokZxCzvd", + "collectionUri": "collection://uri" } ] } @@ -5250,41 +5163,21 @@ } } }, - "/charts/hashrates": { + "/contract-events/transaction-id/{transaction_id}": { "get": { "tags": [ - "Charts" + "Contract events" ], - "summary": "Get hashrate chart in H/s", - "description": "`interval-type` query param: hourly, daily", - "operationId": "getChartsHashrates", + "description": "Get contract events by transaction id", + "operationId": "getContract-eventsTransaction-idTransaction_id", "parameters": [ { - "name": "fromTs", - "in": "query", - "required": true, - "schema": { - "type": "integer", - "format": "int64", - "minimum": "0" - } - }, - { - "name": "toTs", - "in": "query", - "required": true, - "schema": { - "type": "integer", - "format": "int64", - "minimum": "0" - } - }, - { - "name": "interval-type", - "in": "query", + "name": "transaction_id", + "in": "path", "required": true, "schema": { - "$ref": "#/components/schemas/IntervalType" + "type": "string", + "format": "32-byte-hash" } } ], @@ -5295,19 +5188,35 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/Hashrate" + "$ref": "#/components/schemas/Event" } }, "example": [ { - "timestamp": 1611041396892, - "hashrate": "147573952589676412928", - "value": "147573952589676412928" + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "eventIndex": 0, + "fields": [ + { + "type": "Bool", + "value": true + } + ] }, { - "timestamp": 1611041396892, - "hashrate": "147573952589676412928", - "value": "147573952589676412928" + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "eventIndex": 0, + "fields": [ + { + "type": "Bool", + "value": true + } + ] } ] } @@ -5382,41 +5291,41 @@ } } }, - "/charts/transactions-count": { + "/contract-events/contract-address/{contract_address}": { "get": { "tags": [ - "Charts" + "Contract events" ], - "summary": "Get transaction count history", - "description": "`interval-type` query param: hourly, daily", - "operationId": "getChartsTransactions-count", + "description": "Get contract events by contract address", + "operationId": "getContract-eventsContract-addressContract_address", "parameters": [ { - "name": "fromTs", - "in": "query", + "name": "contract_address", + "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int64", - "minimum": "0" + "type": "string", + "format": "address" } }, { - "name": "toTs", + "name": "page", "in": "query", - "required": true, + "description": "Page number", + "required": false, "schema": { "type": "integer", - "format": "int64", - "minimum": "0" + "format": "int32" } }, { - "name": "interval-type", + "name": "limit", "in": "query", - "required": true, + "description": "Number of items per page", + "required": false, "schema": { - "$ref": "#/components/schemas/IntervalType" + "type": "integer", + "format": "int32" } } ], @@ -5427,17 +5336,35 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TimedCount" + "$ref": "#/components/schemas/Event" } }, "example": [ { - "timestamp": 1611041396892, - "totalCountAllChains": 10000000 + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "eventIndex": 0, + "fields": [ + { + "type": "Bool", + "value": true + } + ] }, { - "timestamp": 1611041396892, - "totalCountAllChains": 10000000 + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "eventIndex": 0, + "fields": [ + { + "type": "Bool", + "value": true + } + ] } ] } @@ -5512,41 +5439,50 @@ } } }, - "/charts/transactions-count-per-chain": { + "/contract-events/contract-address/{contract_address}/input-address/{input_address}": { "get": { "tags": [ - "Charts" + "Contract events" ], - "summary": "Get transaction count history per chain", - "description": "`interval-type` query param: hourly, daily", - "operationId": "getChartsTransactions-count-per-chain", + "description": "Get contract events by contract and input addresses", + "operationId": "getContract-eventsContract-addressContract_addressInput-addressInput_address", "parameters": [ { - "name": "fromTs", - "in": "query", + "name": "contract_address", + "in": "path", "required": true, "schema": { - "type": "integer", - "format": "int64", - "minimum": "0" + "type": "string", + "format": "address" } }, { - "name": "toTs", - "in": "query", + "name": "input_address", + "in": "path", "required": true, + "schema": { + "type": "string", + "format": "address" + } + }, + { + "name": "page", + "in": "query", + "description": "Page number", + "required": false, "schema": { "type": "integer", - "format": "int64", - "minimum": "0" + "format": "int32" } }, { - "name": "interval-type", + "name": "limit", "in": "query", - "required": true, + "description": "Number of items per page", + "required": false, "schema": { - "$ref": "#/components/schemas/IntervalType" + "type": "integer", + "format": "int32" } } ], @@ -5557,37 +5493,33 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/PerChainTimedCount" + "$ref": "#/components/schemas/Event" } }, "example": [ { - "timestamp": 1611041396892, - "totalCountPerChain": [ - { - "chainFrom": 1, - "chainTo": 2, - "count": 10000000 - }, + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "eventIndex": 0, + "fields": [ { - "chainFrom": 1, - "chainTo": 2, - "count": 10000000 + "type": "Bool", + "value": true } ] }, { - "timestamp": 1611041396892, - "totalCountPerChain": [ - { - "chainFrom": 1, - "chainTo": 2, - "count": 10000000 - }, + "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", + "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", + "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "eventIndex": 0, + "fields": [ { - "chainFrom": 1, - "chainTo": 2, - "count": 10000000 + "type": "Bool", + "value": true } ] } @@ -5664,21 +5596,21 @@ } } }, - "/contract-events/transaction-id/{transaction_id}": { + "/contracts/{contract_address}/parent": { "get": { "tags": [ - "Contract events" + "Contracts" ], - "description": "Get contract events by transaction id", - "operationId": "getContract-eventsTransaction-idTransaction_id", + "description": "Get contract parent address if exist", + "operationId": "getContractsContract_addressParent", "parameters": [ { - "name": "transaction_id", + "name": "contract_address", "in": "path", "required": true, "schema": { "type": "string", - "format": "32-byte-hash" + "format": "address" } } ], @@ -5687,39 +5619,11 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Event" - } - }, - "example": [ - { - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "eventIndex": 0, - "fields": [ - { - "type": "Bool", - "value": true - } - ] - }, - { - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "eventIndex": 0, - "fields": [ - { - "type": "Bool", - "value": true - } - ] - } - ] + "$ref": "#/components/schemas/ContractParent" + }, + "example": { + "parent": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y" + } } } }, @@ -5792,13 +5696,13 @@ } } }, - "/contract-events/contract-address/{contract_address}": { + "/contracts/{contract_address}/sub-contracts": { "get": { "tags": [ - "Contract events" + "Contracts" ], - "description": "Get contract events by contract address", - "operationId": "getContract-eventsContract-addressContract_address", + "description": "Get sub contract addresses", + "operationId": "getContractsContract_addressSub-contracts", "parameters": [ { "name": "contract_address", @@ -5835,39 +5739,14 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Event" - } + "$ref": "#/components/schemas/SubContracts" }, - "example": [ - { - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "eventIndex": 0, - "fields": [ - { - "type": "Bool", - "value": true - } - ] - }, - { - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "eventIndex": 0, - "fields": [ - { - "type": "Bool", - "value": true - } - ] - } - ] + "example": { + "subContracts": [ + "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", + "22fnZLkZJUSyhXgboirmJktWkEBRk1pV8L6gfpc53hvVM" + ] + } } } }, @@ -5940,50 +5819,41 @@ } } }, - "/contract-events/contract-address/{contract_address}/input-address/{input_address}": { + "/charts/hashrates": { "get": { "tags": [ - "Contract events" + "Charts" ], - "description": "Get contract events by contract and input addresses", - "operationId": "getContract-eventsContract-addressContract_addressInput-addressInput_address", + "summary": "Get hashrate chart in H/s", + "description": "`interval-type` query param: hourly, daily", + "operationId": "getChartsHashrates", "parameters": [ { - "name": "contract_address", - "in": "path", - "required": true, - "schema": { - "type": "string", - "format": "address" - } - }, - { - "name": "input_address", - "in": "path", + "name": "fromTs", + "in": "query", "required": true, "schema": { - "type": "string", - "format": "address" + "type": "integer", + "format": "int64", + "minimum": "0" } }, { - "name": "page", + "name": "toTs", "in": "query", - "description": "Page number", - "required": false, + "required": true, "schema": { "type": "integer", - "format": "int32" + "format": "int64", + "minimum": "0" } }, { - "name": "limit", + "name": "interval-type", "in": "query", - "description": "Number of items per page", - "required": false, + "required": true, "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/IntervalType" } } ], @@ -5994,35 +5864,19 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/Event" + "$ref": "#/components/schemas/Hashrate" } }, "example": [ { - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "eventIndex": 0, - "fields": [ - { - "type": "Bool", - "value": true - } - ] + "timestamp": 1611041396892, + "hashrate": "147573952589676412928", + "value": "147573952589676412928" }, { - "blockHash": "bdaf9dc514ce7d34b6474b8ca10a3dfb93ba997cb9d5ff1ea724ebe2af48abe5", - "txHash": "503bfb16230888af4924aa8f8250d7d348b862e267d75d3147f1998050b6da69", - "contractAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "inputAddress": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "eventIndex": 0, - "fields": [ - { - "type": "Bool", - "value": true - } - ] + "timestamp": 1611041396892, + "hashrate": "147573952589676412928", + "value": "147573952589676412928" } ] } @@ -6097,21 +5951,41 @@ } } }, - "/contracts/{contract_address}/parent": { + "/charts/transactions-count": { "get": { "tags": [ - "Contracts" + "Charts" ], - "description": "Get contract parent address if exist", - "operationId": "getContractsContract_addressParent", + "summary": "Get transaction count history", + "description": "`interval-type` query param: hourly, daily", + "operationId": "getChartsTransactions-count", "parameters": [ { - "name": "contract_address", - "in": "path", + "name": "fromTs", + "in": "query", "required": true, "schema": { - "type": "string", - "format": "address" + "type": "integer", + "format": "int64", + "minimum": "0" + } + }, + { + "name": "toTs", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "format": "int64", + "minimum": "0" + } + }, + { + "name": "interval-type", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/IntervalType" } } ], @@ -6120,11 +5994,21 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContractParent" + "type": "array", + "items": { + "$ref": "#/components/schemas/TimedCount" + } }, - "example": { - "parent": "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y" - } + "example": [ + { + "timestamp": 1611041396892, + "totalCountAllChains": 10000000 + }, + { + "timestamp": 1611041396892, + "totalCountAllChains": 10000000 + } + ] } } }, @@ -6197,41 +6081,41 @@ } } }, - "/contracts/{contract_address}/sub-contracts": { + "/charts/transactions-count-per-chain": { "get": { "tags": [ - "Contracts" + "Charts" ], - "description": "Get sub contract addresses", - "operationId": "getContractsContract_addressSub-contracts", + "summary": "Get transaction count history per chain", + "description": "`interval-type` query param: hourly, daily", + "operationId": "getChartsTransactions-count-per-chain", "parameters": [ { - "name": "contract_address", - "in": "path", + "name": "fromTs", + "in": "query", "required": true, "schema": { - "type": "string", - "format": "address" + "type": "integer", + "format": "int64", + "minimum": "0" } }, { - "name": "page", + "name": "toTs", "in": "query", - "description": "Page number", - "required": false, + "required": true, "schema": { "type": "integer", - "format": "int32" + "format": "int64", + "minimum": "0" } }, { - "name": "limit", + "name": "interval-type", "in": "query", - "description": "Number of items per page", - "required": false, + "required": true, "schema": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/IntervalType" } } ], @@ -6240,14 +6124,43 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubContracts" + "type": "array", + "items": { + "$ref": "#/components/schemas/PerChainTimedCount" + } }, - "example": { - "subContracts": [ - "1AujpupFP4KWeZvqA7itsHY9cLJmx4qTzojVZrg8W9y", - "22fnZLkZJUSyhXgboirmJktWkEBRk1pV8L6gfpc53hvVM" - ] - } + "example": [ + { + "timestamp": 1611041396892, + "totalCountPerChain": [ + { + "chainFrom": 1, + "chainTo": 2, + "count": 10000000 + }, + { + "chainFrom": 1, + "chainTo": 2, + "count": 10000000 + } + ] + }, + { + "timestamp": 1611041396892, + "totalCountPerChain": [ + { + "chainFrom": 1, + "chainTo": 2, + "count": 10000000 + }, + { + "chainFrom": 1, + "chainTo": 2, + "count": 10000000 + } + ] + } + ] } } }, diff --git a/app/src/main/scala/org/alephium/explorer/AppServer.scala b/app/src/main/scala/org/alephium/explorer/AppServer.scala index 9252c16ad..0109fbb6d 100644 --- a/app/src/main/scala/org/alephium/explorer/AppServer.scala +++ b/app/src/main/scala/org/alephium/explorer/AppServer.scala @@ -31,11 +31,13 @@ import org.alephium.explorer.web._ // scalastyle:off magic.number parameter.number object AppServer { + // scalastyle:off method.length def routes( exportTxsNumberThreshold: Int, streamParallelism: Int, maxTimeIntervals: ExplorerConfig.MaxTimeIntervals, - marketConfig: ExplorerConfig.Market + marketConfig: ExplorerConfig.Market, + servicesConfig: ExplorerConfig.Services )(implicit ec: ExecutionContext, dc: DatabaseConfig[PostgresProfile], @@ -57,15 +59,16 @@ object AppServer { maxTimeIntervals.exportTxs ) val transactionServer = new TransactionServer() - val infosServer = new InfosServer(TokenSupplyService, BlockService, TransactionService) + val infosServer = + new InfosServer(TokenSupplyService, BlockService, TransactionService, servicesConfig) val utilsServer: UtilsServer = new UtilsServer() - val chartsServer: ChartsServer = new ChartsServer(maxTimeIntervals.charts) + val chartsServer: ChartsServer = new ChartsServer(maxTimeIntervals.charts, servicesConfig) val tokenServer: TokenServer = new TokenServer(TokenService) - val mempoolServer = new MempoolServer() + val mempoolServer = new MempoolServer(servicesConfig.mempoolSync) val eventServer = new EventServer() val contractServer = new ContractServer() val marketServer = new MarketServer(marketService) - val documentationServer = new DocumentationServer(maxTimeIntervals.exportTxs) + val documentationServer = new DocumentationServer(maxTimeIntervals.exportTxs, servicesConfig) blockServer.routes ++ addressServer.routes ++ diff --git a/app/src/main/scala/org/alephium/explorer/ExplorerState.scala b/app/src/main/scala/org/alephium/explorer/ExplorerState.scala index e5140b24f..7832ae798 100644 --- a/app/src/main/scala/org/alephium/explorer/ExplorerState.scala +++ b/app/src/main/scala/org/alephium/explorer/ExplorerState.scala @@ -97,7 +97,8 @@ sealed trait ExplorerStateRead extends ExplorerState { config.exportTxsNumberThreshold, config.streamParallelism, config.maxTimeInterval, - config.market + config.market, + config.services )( executionContext, database.databaseConfig, diff --git a/app/src/main/scala/org/alephium/explorer/docs/Documentation.scala b/app/src/main/scala/org/alephium/explorer/docs/Documentation.scala index e68a63f10..af07f185e 100644 --- a/app/src/main/scala/org/alephium/explorer/docs/Documentation.scala +++ b/app/src/main/scala/org/alephium/explorer/docs/Documentation.scala @@ -21,7 +21,15 @@ import sttp.apispec.openapi.OpenAPI import sttp.tapir.docs.openapi.OpenAPIDocsInterpreter import org.alephium.explorer.api._ +import org.alephium.explorer.config.ExplorerConfig +@SuppressWarnings( + Array( + "org.wartremover.warts.JavaSerializable", + "org.wartremover.warts.Product", + "org.wartremover.warts.Serializable" + ) +) trait Documentation extends BlockEndpoints with TransactionEndpoints @@ -36,59 +44,126 @@ trait Documentation with UtilsEndpoints with OpenAPIDocsInterpreter { - lazy val docs: OpenAPI = addComponents( - toOpenAPI( + def servicesConfig: ExplorerConfig.Services + + private lazy val blocks = List( + listBlocks, + getBlockByHash, + getBlockTransactions + ) + + private lazy val transactions = List( + getTransactionById + ) + + private lazy val addresses = List( + getTransactionById, + getAddressInfo, + getTransactionsByAddress, + getTransactionsByAddresses, + getTransactionsByAddressTimeRanged, + getTotalTransactionsByAddress, + addressMempoolTransactions, + getAddressBalance, + listAddressTokens, + listAddressTokenTransactions, + getAddressTokenBalance, + listAddressTokensBalance, + areAddressesActive, + exportTransactionsCsvByAddress, + getAddressAmountHistoryDEPRECATED, + getAddressAmountHistory + ) + + private lazy val infos = List( + getInfos, + getHeights, + getAverageBlockTime + ) + + private lazy val tokens = List( + listTokens, + listTokenTransactions, + listTokenAddresses, + listTokenInfo, + listFungibleTokenMetadata, + listNFTMetadata, + listNFTCollectionMetadata + ) + + private lazy val events = List( + getEventsByTxId, + getEventsByContractAddress, + getEventsByContractAndInputAddress + ) + + private lazy val contracts = List( + getParentAddress, + getSubContracts + ) + + private lazy val mempool = + if (servicesConfig.mempoolSync.enable) { + List( + listMempoolTransactions + ) + } else { + List.empty + } + + private lazy val tokenSupply = + if (servicesConfig.tokenSupply.enable) { List( - listBlocks, - getBlockByHash, - getBlockTransactions, - getTransactionById, - getAddressInfo, - getTransactionsByAddress, - getTransactionsByAddresses, - getTransactionsByAddressTimeRanged, - getTotalTransactionsByAddress, - addressMempoolTransactions, - getAddressBalance, - listAddressTokens, - listAddressTokenTransactions, - getAddressTokenBalance, - listAddressTokensBalance, - areAddressesActive, - exportTransactionsCsvByAddress, - getAddressAmountHistoryDEPRECATED, - getAddressAmountHistory, - getInfos, - getHeights, - listMempoolTransactions, - listTokens, - listTokenTransactions, - listTokenAddresses, listTokenSupply, - listTokenInfo, - listFungibleTokenMetadata, - listNFTMetadata, - listNFTCollectionMetadata, - getTotalSupply, getCirculatingSupply, + getTotalSupply, getReservedSupply, - getLockedSupply, - getTotalTransactions, - getAverageBlockTime, - getHashrates, - getAllChainsTxCount, - getPerChainTxCount, - getEventsByTxId, - getEventsByContractAddress, - getEventsByContractAndInputAddress, - getParentAddress, - getSubContracts, - getPrices, - getPriceChart, - sanityCheck, - changeGlobalLogLevel, - changeLogConfig - ), + getLockedSupply + ) + } else { + List.empty + } + + private lazy val hashrate = + if (servicesConfig.hashrate.enable) { + List(getHashrates) + } else { + List.empty + } + + private lazy val txHistory = + if (servicesConfig.txHistory.enable) { + List(getAllChainsTxCount, getPerChainTxCount) + } else { + List.empty + } + + private lazy val market = List( + getPrices, + getPriceChart + ) + + private lazy val utils = List( + sanityCheck, + changeGlobalLogLevel, + changeLogConfig + ) + + lazy val docs: OpenAPI = addComponents( + toOpenAPI( + blocks ++ + transactions ++ + mempool ++ + addresses ++ + infos ++ + tokenSupply ++ + tokens ++ + events ++ + contracts ++ + hashrate ++ + txHistory ++ + market ++ + utils, "Alephium Explorer API", "1.0" ) diff --git a/app/src/main/scala/org/alephium/explorer/web/ChartsServer.scala b/app/src/main/scala/org/alephium/explorer/web/ChartsServer.scala index c282c2876..648574c68 100644 --- a/app/src/main/scala/org/alephium/explorer/web/ChartsServer.scala +++ b/app/src/main/scala/org/alephium/explorer/web/ChartsServer.scala @@ -32,39 +32,51 @@ import org.alephium.explorer.config.ExplorerConfig import org.alephium.explorer.service.{HashrateService, TransactionHistoryService} class ChartsServer( - maxTimeInterval: ExplorerConfig.MaxTimeInterval + maxTimeInterval: ExplorerConfig.MaxTimeInterval, + services: ExplorerConfig.Services )(implicit val executionContext: ExecutionContext, dc: DatabaseConfig[PostgresProfile] ) extends Server with ChartsEndpoints { - val routes: ArraySeq[Router => Route] = - ArraySeq( - route(getHashrates.serverLogic[Future] { case (timeInterval, interval) => - validateTimeInterval(timeInterval, interval) { - HashrateService.get(timeInterval.from, timeInterval.to, interval) - } - }), - route(getAllChainsTxCount.serverLogic[Future] { case (timeInterval, interval) => - validateTimeInterval(timeInterval, interval) { - TransactionHistoryService - .getAllChains(timeInterval.from, timeInterval.to, interval) - .map { seq => - seq.map { case (timestamp, count) => - TimedCount(timestamp, count) + val hashrateRoutes: Option[ArraySeq[Router => Route]] = + Option.when(services.hashrate.enable)( + ArraySeq( + route(getHashrates.serverLogic[Future] { case (timeInterval, interval) => + validateTimeInterval(timeInterval, interval) { + HashrateService.get(timeInterval.from, timeInterval.to, interval) + } + }) + ) + ) + + val txHistoryRoutes: Option[ArraySeq[Router => Route]] = + Option.when(services.txHistory.enable)( + ArraySeq( + route(getAllChainsTxCount.serverLogic[Future] { case (timeInterval, interval) => + validateTimeInterval(timeInterval, interval) { + TransactionHistoryService + .getAllChains(timeInterval.from, timeInterval.to, interval) + .map { seq => + seq.map { case (timestamp, count) => + TimedCount(timestamp, count) + } } - } - } - }), - route(getPerChainTxCount.serverLogic[Future] { case (timeInterval, interval) => - validateTimeInterval(timeInterval, interval) { - TransactionHistoryService - .getPerChain(timeInterval.from, timeInterval.to, interval) - } - }) + } + }), + route(getPerChainTxCount.serverLogic[Future] { case (timeInterval, interval) => + validateTimeInterval(timeInterval, interval) { + TransactionHistoryService + .getPerChain(timeInterval.from, timeInterval.to, interval) + } + }) + ) ) + val routes: ArraySeq[Router => Route] = + hashrateRoutes.getOrElse(ArraySeq.empty) ++ txHistoryRoutes.getOrElse(ArraySeq.empty) + private def validateTimeInterval[A](timeInterval: TimeInterval, intervalType: IntervalType)( contd: => Future[A] ): Future[Either[ApiError[_ <: StatusCode], A]] = diff --git a/app/src/main/scala/org/alephium/explorer/web/DocumentationServer.scala b/app/src/main/scala/org/alephium/explorer/web/DocumentationServer.scala index daba85853..87f180fa1 100644 --- a/app/src/main/scala/org/alephium/explorer/web/DocumentationServer.scala +++ b/app/src/main/scala/org/alephium/explorer/web/DocumentationServer.scala @@ -22,11 +22,15 @@ import io.vertx.ext.web._ import org.alephium.api.OpenAPIWriters.openApiJson import org.alephium.explorer.GroupSetting +import org.alephium.explorer.config.ExplorerConfig import org.alephium.explorer.docs.Documentation import org.alephium.http.SwaggerUI import org.alephium.util.Duration -class DocumentationServer(val maxTimeIntervalExportTxs: Duration)(implicit +class DocumentationServer( + val maxTimeIntervalExportTxs: Duration, + val servicesConfig: ExplorerConfig.Services +)(implicit groupSetting: GroupSetting ) extends Server with Documentation { diff --git a/app/src/main/scala/org/alephium/explorer/web/InfosServer.scala b/app/src/main/scala/org/alephium/explorer/web/InfosServer.scala index 4c0943aba..a282fbf53 100644 --- a/app/src/main/scala/org/alephium/explorer/web/InfosServer.scala +++ b/app/src/main/scala/org/alephium/explorer/web/InfosServer.scala @@ -30,6 +30,7 @@ import org.alephium.explorer.{BuildInfo, GroupSetting} import org.alephium.explorer.api.InfosEndpoints import org.alephium.explorer.api.model.{ExplorerInfo, TokenSupply} import org.alephium.explorer.cache.{AsyncReloadingCache, BlockCache, TransactionCache} +import org.alephium.explorer.config.ExplorerConfig import org.alephium.explorer.persistence.DBRunner import org.alephium.explorer.persistence.model.AppState import org.alephium.explorer.persistence.queries.AppStateQueries @@ -41,7 +42,8 @@ import org.alephium.util.{TimeStamp, U256} class InfosServer( tokenSupplyService: TokenSupplyService, blockService: BlockService, - transactionService: TransactionService + transactionService: TransactionService, + config: ExplorerConfig.Services )(implicit val executionContext: ExecutionContext, dc: DatabaseConfig[PostgresProfile], @@ -51,55 +53,59 @@ class InfosServer( ) extends Server with InfosEndpoints { - // scalafmt is struggling on this one, maybe latest version wil work. - // format: off - val routes: ArraySeq[Router=>Route] = + val tokenSupplyRoutes: Option[ArraySeq[Router => Route]] = + Option + .when(config.tokenSupply.enable)( + ArraySeq( + route(listTokenSupply.serverLogicSuccess[Future] { pagination => + tokenSupplyService.listTokenSupply(pagination) + }), + route(getCirculatingSupply.serverLogicSuccess[Future] { _ => + getLatestTokenSupply() + .map { supply => + val circulating = supply.map(_.circulating).getOrElse(U256.Zero) + toALPH(circulating) + } + }), + route(getTotalSupply.serverLogicSuccess[Future] { _ => + getLatestTokenSupply() + .map { supply => + val total = supply.map(_.total).getOrElse(U256.Zero) + toALPH(total) + } + }), + route(getReservedSupply.serverLogicSuccess[Future] { _ => + getLatestTokenSupply() + .map { supply => + val reserved = supply.map(_.reserved).getOrElse(U256.Zero) + toALPH(reserved) + } + }), + route(getLockedSupply.serverLogicSuccess[Future] { _ => + getLatestTokenSupply() + .map { supply => + val locked = supply.map(_.locked).getOrElse(U256.Zero) + toALPH(locked) + } + }) + ) + ) + + val routes: ArraySeq[Router => Route] = ArraySeq( route(getInfos.serverLogicSuccess[Future] { _ => getExplorerInfo() - }) , - route(listTokenSupply.serverLogicSuccess[Future] { pagination => - tokenSupplyService.listTokenSupply(pagination) - }) , - route(getCirculatingSupply.serverLogicSuccess[Future] { _ => - getLatestTokenSupply() - .map { supply => - val circulating = supply.map(_.circulating).getOrElse(U256.Zero) - toALPH(circulating) - } - }) , - route(getTotalSupply.serverLogicSuccess[Future] { _ => - getLatestTokenSupply() - .map { supply => - val total = supply.map(_.total).getOrElse(U256.Zero) - toALPH(total) - } - }) , - route(getReservedSupply.serverLogicSuccess[Future] { _ => - getLatestTokenSupply() - .map { supply => - val reserved = supply.map(_.reserved).getOrElse(U256.Zero) - toALPH(reserved) - } - }) , - route(getLockedSupply.serverLogicSuccess[Future] { _ => - getLatestTokenSupply() - .map { supply => - val locked = supply.map(_.locked).getOrElse(U256.Zero) - toALPH(locked) - } - }) , - route(getHeights.serverLogicSuccess[Future]{ _ => - blockService.listMaxHeights() - }) , - route(getTotalTransactions.serverLogicSuccess[Future]{ _=> + }), + route(getHeights.serverLogicSuccess[Future] { _ => + blockService.listMaxHeights() + }), + route(getTotalTransactions.serverLogicSuccess[Future] { _ => Future(transactionService.getTotalNumber()) }), - route(getAverageBlockTime.serverLogicSuccess[Future]{ _=> + route(getAverageBlockTime.serverLogicSuccess[Future] { _ => blockService.getAverageBlockTime() }) - ) - // format: on + ) ++ tokenSupplyRoutes.getOrElse(ArraySeq.empty) def getExplorerInfo()(implicit executionContext: ExecutionContext, diff --git a/app/src/main/scala/org/alephium/explorer/web/MempoolServer.scala b/app/src/main/scala/org/alephium/explorer/web/MempoolServer.scala index a4b2cf618..c56d8672e 100644 --- a/app/src/main/scala/org/alephium/explorer/web/MempoolServer.scala +++ b/app/src/main/scala/org/alephium/explorer/web/MempoolServer.scala @@ -24,17 +24,23 @@ import slick.basic.DatabaseConfig import slick.jdbc.PostgresProfile import org.alephium.explorer.api.MempoolEndpoints +import org.alephium.explorer.config.ExplorerConfig import org.alephium.explorer.service.TransactionService -class MempoolServer(implicit +class MempoolServer(config: ExplorerConfig.Services.MempoolSync)(implicit val executionContext: ExecutionContext, dc: DatabaseConfig[PostgresProfile] ) extends Server with MempoolEndpoints { - val routes: ArraySeq[Router => Route] = ArraySeq( - route(listMempoolTransactions.serverLogicSuccess[Future] { pagination => - TransactionService - .listMempoolTransactions(pagination) - }) - ) + val routes: ArraySeq[Router => Route] = + if (config.enable) { + ArraySeq( + route(listMempoolTransactions.serverLogicSuccess[Future] { pagination => + TransactionService + .listMempoolTransactions(pagination) + }) + ) + } else { + ArraySeq() + } } diff --git a/app/src/test/scala/org/alephium/explorer/ConfigDefaults.scala b/app/src/test/scala/org/alephium/explorer/ConfigDefaults.scala index c226708e9..3de242199 100644 --- a/app/src/test/scala/org/alephium/explorer/ConfigDefaults.scala +++ b/app/src/test/scala/org/alephium/explorer/ConfigDefaults.scala @@ -16,6 +16,8 @@ package org.alephium.explorer +import java.time.LocalTime + import org.alephium.explorer.config.ExplorerConfig._ import org.alephium.util.Duration @@ -37,4 +39,29 @@ object ConfigDefaults { ), exportTxs = Duration.ofDaysUnsafe(366) ) + + val servicesConfig: Services = Services( + Services.BlockflowSync( + syncPeriod = Duration.ofSecondsUnsafe(5).asScala + ), + Services.MempoolSync( + enable = true, + syncPeriod = Duration.ofSecondsUnsafe(5).asScala + ), + Services.TokenSupply( + enable = true, + scheduleTime = LocalTime.parse("02:00") + ), + Services.Hashrate( + enable = true, + syncPeriod = Duration.ofMinutesUnsafe(60).asScala + ), + Services.TxHistory( + enable = true, + syncPeriod = Duration.ofMinutesUnsafe(15).asScala + ), + Services.Finalizer( + syncPeriod = Duration.ofMinutesUnsafe(10).asScala + ) + ) } diff --git a/app/src/test/scala/org/alephium/explorer/web/ChartsServerSpec.scala b/app/src/test/scala/org/alephium/explorer/web/ChartsServerSpec.scala index 822eefa77..14caaad4e 100644 --- a/app/src/test/scala/org/alephium/explorer/web/ChartsServerSpec.scala +++ b/app/src/test/scala/org/alephium/explorer/web/ChartsServerSpec.scala @@ -30,7 +30,8 @@ class ChartsServerSpec() with HttpServerFixture { val chartServer = new ChartsServer( - maxTimeInterval = ConfigDefaults.maxTimeIntervals.charts + maxTimeInterval = ConfigDefaults.maxTimeIntervals.charts, + services = ConfigDefaults.servicesConfig ) override val routes = chartServer.routes diff --git a/app/src/test/scala/org/alephium/explorer/web/InfosServerSpec.scala b/app/src/test/scala/org/alephium/explorer/web/InfosServerSpec.scala index 7939eef3c..5343a5fd4 100644 --- a/app/src/test/scala/org/alephium/explorer/web/InfosServerSpec.scala +++ b/app/src/test/scala/org/alephium/explorer/web/InfosServerSpec.scala @@ -98,7 +98,7 @@ class InfosServerSpec() } val infoServer = - new InfosServer(tokenSupplyService, blockService, transactionService) + new InfosServer(tokenSupplyService, blockService, transactionService, servicesConfig) val routes = infoServer.routes diff --git a/app/src/test/scala/org/alephium/explorer/web/MempoolServerSpec.scala b/app/src/test/scala/org/alephium/explorer/web/MempoolServerSpec.scala index c52a688fa..f3019c28a 100644 --- a/app/src/test/scala/org/alephium/explorer/web/MempoolServerSpec.scala +++ b/app/src/test/scala/org/alephium/explorer/web/MempoolServerSpec.scala @@ -18,7 +18,7 @@ package org.alephium.explorer.web import org.scalacheck.Gen -import org.alephium.explorer.{AlephiumActorSpecLike, HttpServerFixture} +import org.alephium.explorer.{AlephiumActorSpecLike, ConfigDefaults, HttpServerFixture} import org.alephium.explorer.GenApiModel._ import org.alephium.explorer.HttpFixture._ import org.alephium.explorer.api.model._ @@ -31,7 +31,7 @@ class MempoolServerSpec() with HttpServerFixture with DatabaseFixtureForAll { - val utxServer = new MempoolServer() + val utxServer = new MempoolServer(ConfigDefaults.servicesConfig.mempoolSync) val routes = utxServer.routes diff --git a/tools/src/main/scala/org/alephium/tools/OpenApiUpdate.scala b/tools/src/main/scala/org/alephium/tools/OpenApiUpdate.scala index 6725f17b4..9f0157fd2 100644 --- a/tools/src/main/scala/org/alephium/tools/OpenApiUpdate.scala +++ b/tools/src/main/scala/org/alephium/tools/OpenApiUpdate.scala @@ -16,7 +16,10 @@ package org.alephium.tools +import com.typesafe.config.ConfigFactory + import org.alephium.api.OpenAPIWriters.openApiJson +import org.alephium.explorer.config.ExplorerConfig import org.alephium.explorer.docs.Documentation import org.alephium.util.{discard, Duration} @@ -25,6 +28,11 @@ object OpenApiUpdate { discard { new Documentation { + private val typesafeConfig = ConfigFactory.load() + + val config: ExplorerConfig = ExplorerConfig.load(typesafeConfig) + val servicesConfig = config.services + // scalastyle:off magic.number val groupNum = 4 val maxTimeIntervalExportTxs: Duration = Duration.ofDaysUnsafe(366)