From 17c0da4bc5c1931074d0efafeebd36d98ee43063 Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Tue, 12 Jul 2022 17:19:48 +0100 Subject: [PATCH 01/86] Cast timestamps to integers (#60) --- specs/results/koiosapi-guild.yaml | 19 +++++++++---------- specs/results/koiosapi-mainnet.yaml | 19 +++++++++---------- specs/results/koiosapi-testnet.yaml | 19 +++++++++---------- specs/templates/4-api-schemas.yaml | 19 +++++++++---------- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 0b68fa46..0f084de6 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1487,7 +1487,7 @@ components: example: 45000000000000000 description: Maximum smallest units (lovelaces) supply for the blockchain systemstart: - type: number + type: integer description: UNIX timestamp of the first block (genesis) on chain example: 1506203091 activeslotcoeff: @@ -1879,19 +1879,19 @@ components: description: Number of blocks created in epoch example: 17321 start_time: - type: number + type: integer description: UNIX timestamp of the epoch start example: 1506203091 end_time: - type: number + type: integer description: UNIX timestamp of the epoch end example: 1506635091 first_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's first block example: 1506635091 last_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's last block example: 1506635091 active_stake: @@ -2107,7 +2107,7 @@ components: description: Block size in bytes example: 79109 block_time: - type: number + type: integer description: UNIX timestamp of the block example: 1506635091 tx_count: @@ -2386,7 +2386,7 @@ components: description: Overall slot number (slots from genesis block of chain) example: 42325043 tx_timestamp: - type: number + type: integer description: UNIX timestamp of the transaction example: 1506635091 tx_block_index: @@ -2636,8 +2636,7 @@ components: "version": 1, "copyright": "...", "publisher": ["p...o"], - ? "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d" - : {}, + "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, }, } certificates: @@ -3049,7 +3048,7 @@ components: type: string example: "35000" creation_time: - type: number + type: integer description: UNIX timestamp of the first asset mint example: 1506635091 asset_history: diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 17585f65..afd7df31 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1487,7 +1487,7 @@ components: example: 45000000000000000 description: Maximum smallest units (lovelaces) supply for the blockchain systemstart: - type: number + type: integer description: UNIX timestamp of the first block (genesis) on chain example: 1506203091 activeslotcoeff: @@ -1879,19 +1879,19 @@ components: description: Number of blocks created in epoch example: 17321 start_time: - type: number + type: integer description: UNIX timestamp of the epoch start example: 1506203091 end_time: - type: number + type: integer description: UNIX timestamp of the epoch end example: 1506635091 first_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's first block example: 1506635091 last_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's last block example: 1506635091 active_stake: @@ -2107,7 +2107,7 @@ components: description: Block size in bytes example: 79109 block_time: - type: number + type: integer description: UNIX timestamp of the block example: 1506635091 tx_count: @@ -2386,7 +2386,7 @@ components: description: Overall slot number (slots from genesis block of chain) example: 42325043 tx_timestamp: - type: number + type: integer description: UNIX timestamp of the transaction example: 1506635091 tx_block_index: @@ -2636,8 +2636,7 @@ components: "version": 1, "copyright": "...", "publisher": ["p...o"], - ? "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d" - : {}, + "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, }, } certificates: @@ -3049,7 +3048,7 @@ components: type: string example: "35000" creation_time: - type: number + type: integer description: UNIX timestamp of the first asset mint example: 1506635091 asset_history: diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index a7c48a4d..c986baf1 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1487,7 +1487,7 @@ components: example: 45000000000000000 description: Maximum smallest units (lovelaces) supply for the blockchain systemstart: - type: number + type: integer description: UNIX timestamp of the first block (genesis) on chain example: 1506203091 activeslotcoeff: @@ -1879,19 +1879,19 @@ components: description: Number of blocks created in epoch example: 17321 start_time: - type: number + type: integer description: UNIX timestamp of the epoch start example: 1506203091 end_time: - type: number + type: integer description: UNIX timestamp of the epoch end example: 1506635091 first_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's first block example: 1506635091 last_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's last block example: 1506635091 active_stake: @@ -2107,7 +2107,7 @@ components: description: Block size in bytes example: 79109 block_time: - type: number + type: integer description: UNIX timestamp of the block example: 1506635091 tx_count: @@ -2386,7 +2386,7 @@ components: description: Overall slot number (slots from genesis block of chain) example: 42325043 tx_timestamp: - type: number + type: integer description: UNIX timestamp of the transaction example: 1506635091 tx_block_index: @@ -2636,8 +2636,7 @@ components: "version": 1, "copyright": "...", "publisher": ["p...o"], - ? "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d" - : {}, + "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, }, } certificates: @@ -3049,7 +3048,7 @@ components: type: string example: "35000" creation_time: - type: number + type: integer description: UNIX timestamp of the first asset mint example: 1506635091 asset_history: diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 53426ce5..8ddb036c 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -50,7 +50,7 @@ schemas: example: 45000000000000000 description: Maximum smallest units (lovelaces) supply for the blockchain systemstart: - type: number + type: integer description: UNIX timestamp of the first block (genesis) on chain example: 1506203091 activeslotcoeff: @@ -440,19 +440,19 @@ schemas: description: Number of blocks created in epoch example: 17321 start_time: - type: number + type: integer description: UNIX timestamp of the epoch start example: 1506203091 end_time: - type: number + type: integer description: UNIX timestamp of the epoch end example: 1506635091 first_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's first block example: 1506635091 last_block_time: - type: number + type: integer description: UNIX timestamp of the epoch's last block example: 1506635091 active_stake: @@ -668,7 +668,7 @@ schemas: description: Block size in bytes example: 79109 block_time: - type: number + type: integer description: UNIX timestamp of the block example: 1506635091 tx_count: @@ -947,7 +947,7 @@ schemas: description: Overall slot number (slots from genesis block of chain) example: 42325043 tx_timestamp: - type: number + type: integer description: UNIX timestamp of the transaction example: 1506635091 tx_block_index: @@ -1197,8 +1197,7 @@ schemas: "version": 1, "copyright": "...", "publisher": ["p...o"], - ? "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d" - : {}, + "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, }, } certificates: @@ -1610,7 +1609,7 @@ schemas: type: string example: "35000" creation_time: - type: number + type: integer description: UNIX timestamp of the first asset mint example: 1506635091 asset_history: From 1300b8f6964c7fd65fd7577c624c49ea4fe7ffd2 Mon Sep 17 00:00:00 2001 From: Huth S0lo <78839856+huths0lo@users.noreply.github.com> Date: Mon, 25 Jul 2022 13:45:30 -0700 Subject: [PATCH 02/86] Topology - Update Nodes for Huth (#63) * Update topology-mainnet.json Updating DNS Name for Digital Syndicate Mainnet Node * Update topology-guild.json * Update topology-testnet.json Co-authored-by: RdLrT <3169068+rdlrt@users.noreply.github.com> --- topology/topology-guild.json | 2 +- topology/topology-mainnet.json | 2 +- topology/topology-testnet.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/topology/topology-guild.json b/topology/topology-guild.json index a053073b..452c4012 100644 --- a/topology/topology-guild.json +++ b/topology/topology-guild.json @@ -5,7 +5,7 @@ {"name":"damjan-ssl","addr":"eden-guildnet.koios.rest","port":8453,"ssl":"true"}, {"name":"homer","addr":"95.216.188.94","port":8053}, {"name":"gufmar","addr":"185.161.193.105","port":6029}, - {"name":"HuthS0lo-ssl","addr":"dbsync-guild1.digitalsyndicate.io","port":8453,"ssl":"true"} + {"name":"HuthS0lo-ssl","addr":"koios-guild.digitalsyndicate.io","port":8453,"ssl":"true"} ], "Consumers": [] } diff --git a/topology/topology-mainnet.json b/topology/topology-mainnet.json index 45cb7fbf..62d8be32 100644 --- a/topology/topology-mainnet.json +++ b/topology/topology-mainnet.json @@ -7,7 +7,7 @@ { "name": "homer-redoracle", "addr": "194.233.71.104", "port": 8053}, { "name": "docker", "addr": "92.204.53.48", "port": 8053}, { "name": "rdlrt2", "addr": "194.36.145.157", "port": 8053}, - { "name": "HuthS0lo1-ssl", "addr": "db-sync1.digitalsyndicate.io", "port": 8453, "ssl": "true"}, + { "name": "HuthS0lo1-ssl", "addr": "koios-mainnet.digitalsyndicate.io", "port": 8453, "ssl": "true"}, { "name": "reqlez", "addr": "192.234.196.168", "port": 8053} ], "Consumers": [] diff --git a/topology/topology-testnet.json b/topology/topology-testnet.json index 2defff88..9482954b 100644 --- a/topology/topology-testnet.json +++ b/topology/topology-testnet.json @@ -4,7 +4,7 @@ {"name":"damjan","addr":"195.201.129.190","port":8053}, {"name":"homer","addr":"95.216.173.194","port":8053}, {"name":"rdlrt","addr":"89.58.43.194","port":8053}, - {"name":"HuthS0lo1-ssl","addr":"dbsync-testnet1.digitalsyndicate.io","port":8453,"ssl":"true"}, + {"name":"HuthS0lo1-ssl","addr":"koios-testnet.digitalsyndicate.io","port":8453,"ssl":"true"}, {"name":"reqlez","addr":"192.234.196.167","port":8053} ], "Consumers": [] From 835dbdeb2537697dbc61643ec9c911783751d9fd Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:58:34 +1000 Subject: [PATCH 03/86] Fix schema inconsistencies reported by Dudi, re-use definitions where possible (#65) Co-authored-by: Dostrelith --- specs/results/koiosapi-guild.yaml | 310 +++++----------------------- specs/results/koiosapi-mainnet.yaml | 310 +++++----------------------- specs/results/koiosapi-testnet.yaml | 310 +++++----------------------- specs/templates/4-api-schemas.yaml | 306 +++++---------------------- 4 files changed, 191 insertions(+), 1045 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 0f084de6..dcd7292e 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1441,25 +1441,15 @@ components: items: properties: hash: - type: string - description: Block Hash in hex - example: 3cc8cfdb2d68fdb2a467292bf0acda7b91ab677741e3e1c1dc111f5be0cef0fe + $ref: "#/components/schemas/blocks/items/properties/hash" epoch_no: - type: integer - description: Epoch number - example: 294 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" abs_slot: - type: integer - description: Absolute Slot number (slots not divided into epochs) - example: 41997413 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" epoch_slot: - type: integer - description: Slot number within Epoch - example: 352613 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" block_no: - type: integer - description: Block Height number on chain - example: 6338276 + $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" genesis: @@ -1620,9 +1610,7 @@ components: description: Pool ID (Hex format) example: a532904ca60e13e88437b58e7c6ff66b8d5e7ec8d3f4b9e4be7820ec active_epoch_no: - type: integer - description: Block number on chain where transaction was included - example: 6354154 + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string description: Pool VRF key hash @@ -1986,7 +1974,7 @@ components: description: The decentralisation parameter (1 fully centralised, 0 fully decentralised) example: 0.1 nullable: true - entropy: + extra_entropy: type: string description: The hash of 32-byte string of extra random-ness added into the protocol's entropy pool example: d982e06fd33e7440b43cefad529b7ecafbaa255e38178ad4189a37e4ce9bf1fa @@ -2072,9 +2060,9 @@ components: description: The maximum number of collateral inputs allowed in a transaction example: 3 nullable: true - coins_per_utxo_word: + coins_per_utxo_size: type: integer - description: The cost per UTxO word + description: The cost per UTxO size example: 34482 nullable: true blocks: @@ -2127,6 +2115,10 @@ components: type: integer description: Counter value of the operational certificate used to create this block example: 8 + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" block_info: type: array items: @@ -2329,7 +2321,7 @@ components: type: object properties: asset_policy: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" quantity: @@ -2366,25 +2358,15 @@ components: description: Hash identifier of the transaction example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e block_hash: - type: string - description: Hash of Block in which transaction was included - example: 90062dfc314c7dc3430922a48f79032a63032206fdca2dfd144cf0930d4aa426 + $ref: "#/components/schemas/blocks/items/properties/hash" block_height: - type: integer - description: Block number on chain where transaction was included - example: 6354154 + $ref: "#/components/schemas/blocks/items/properties/block_height" epoch_no: - type: integer - description: Epoch number - example: 295 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: - type: integer - description: Slot number within epoch - example: 248243 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" absolute_slot: - type: integer - description: Overall slot number (slots from genesis block of chain) - example: 42325043 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" tx_timestamp: type: integer description: UNIX timestamp of the transaction @@ -2418,7 +2400,7 @@ components: description: Slot after which transaction cannot be validated example: 42332172 nullable: true - collaterals: + collateral_inputs: type: array description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) nullable: true @@ -2461,13 +2443,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Asset balance on the selected input transaction @@ -2509,20 +2487,17 @@ components: asset_list: type: array nullable: true - description: An array of assets contained on input UTxO + description: An array of assets on the UTxO items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - description: Asset balance on the selected input transaction + nullable: true + description: Quantity of assets on the UTxO example: 1 outputs: type: array @@ -2559,26 +2534,7 @@ components: description: Total sum on the output address example: 157832856 asset_list: - type: array - nullable: true - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - nullable: true - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - nullable: true - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - nullable: true - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2604,13 +2560,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723036363333 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Sum of minted assets (negative on burn) @@ -2626,19 +2578,7 @@ components: description: Metadata key (index) example: "0" json: - type: object - description: A JSON containing details about metadata within transaction - nullable: true - example: - { - "721": - { - "version": 1, - "copyright": "...", - "publisher": ["p...o"], - "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, - }, - } + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -2760,120 +2700,18 @@ components: items: properties: tx_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: - type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs" outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - type: array - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/outputs" tx_metadata: type: array nullable: true items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" metadata: type: object nullable: true @@ -2894,9 +2732,7 @@ components: items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -2917,7 +2753,7 @@ components: type: object properties: policy_id: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_names: type: object properties: @@ -2951,13 +2787,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" total_transactions: type: integer description: Total number of transactions including the given asset @@ -2976,10 +2808,12 @@ components: properties: policy_id: type: string + nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: type: string + nullable: true description: Asset Name (hex) example: 444f4e545350414d asset_name_ascii: @@ -3056,13 +2890,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" minting_txs: type: array description: Array of all mint/burn transactions for an asset @@ -3084,59 +2914,15 @@ components: items: properties: asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" asset_name_ascii: - type: string - description: Asset Name (ASCII) - example: DONTSPAM + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: - type: string - description: The CIP14 fingerprint of the asset - example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_tx_metadata: - type: object - nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: - type: object - description: Asset metadata registered on the Cardano Token Registry - nullable: true - properties: - name: - type: string - example: Rackmob - description: - type: string - example: Metaverse Blockchain Cryptocurrency. - ticker: - type: string - example: MOB - url: - type: string - example: https://www.rackmob.com/ - logo: - type: string - description: A PNG image file as a byte string - example: iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADnmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc - decimals: - type: integer - example: 0 + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" total_supply: type: string example: "35000" diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index afd7df31..fbca9219 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1441,25 +1441,15 @@ components: items: properties: hash: - type: string - description: Block Hash in hex - example: 3cc8cfdb2d68fdb2a467292bf0acda7b91ab677741e3e1c1dc111f5be0cef0fe + $ref: "#/components/schemas/blocks/items/properties/hash" epoch_no: - type: integer - description: Epoch number - example: 294 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" abs_slot: - type: integer - description: Absolute Slot number (slots not divided into epochs) - example: 41997413 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" epoch_slot: - type: integer - description: Slot number within Epoch - example: 352613 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" block_no: - type: integer - description: Block Height number on chain - example: 6338276 + $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" genesis: @@ -1620,9 +1610,7 @@ components: description: Pool ID (Hex format) example: a532904ca60e13e88437b58e7c6ff66b8d5e7ec8d3f4b9e4be7820ec active_epoch_no: - type: integer - description: Block number on chain where transaction was included - example: 6354154 + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string description: Pool VRF key hash @@ -1986,7 +1974,7 @@ components: description: The decentralisation parameter (1 fully centralised, 0 fully decentralised) example: 0.1 nullable: true - entropy: + extra_entropy: type: string description: The hash of 32-byte string of extra random-ness added into the protocol's entropy pool example: d982e06fd33e7440b43cefad529b7ecafbaa255e38178ad4189a37e4ce9bf1fa @@ -2072,9 +2060,9 @@ components: description: The maximum number of collateral inputs allowed in a transaction example: 3 nullable: true - coins_per_utxo_word: + coins_per_utxo_size: type: integer - description: The cost per UTxO word + description: The cost per UTxO size example: 34482 nullable: true blocks: @@ -2127,6 +2115,10 @@ components: type: integer description: Counter value of the operational certificate used to create this block example: 8 + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" block_info: type: array items: @@ -2329,7 +2321,7 @@ components: type: object properties: asset_policy: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" quantity: @@ -2366,25 +2358,15 @@ components: description: Hash identifier of the transaction example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e block_hash: - type: string - description: Hash of Block in which transaction was included - example: 90062dfc314c7dc3430922a48f79032a63032206fdca2dfd144cf0930d4aa426 + $ref: "#/components/schemas/blocks/items/properties/hash" block_height: - type: integer - description: Block number on chain where transaction was included - example: 6354154 + $ref: "#/components/schemas/blocks/items/properties/block_height" epoch_no: - type: integer - description: Epoch number - example: 295 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: - type: integer - description: Slot number within epoch - example: 248243 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" absolute_slot: - type: integer - description: Overall slot number (slots from genesis block of chain) - example: 42325043 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" tx_timestamp: type: integer description: UNIX timestamp of the transaction @@ -2418,7 +2400,7 @@ components: description: Slot after which transaction cannot be validated example: 42332172 nullable: true - collaterals: + collateral_inputs: type: array description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) nullable: true @@ -2461,13 +2443,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Asset balance on the selected input transaction @@ -2509,20 +2487,17 @@ components: asset_list: type: array nullable: true - description: An array of assets contained on input UTxO + description: An array of assets on the UTxO items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - description: Asset balance on the selected input transaction + nullable: true + description: Quantity of assets on the UTxO example: 1 outputs: type: array @@ -2559,26 +2534,7 @@ components: description: Total sum on the output address example: 157832856 asset_list: - type: array - nullable: true - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - nullable: true - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - nullable: true - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - nullable: true - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2604,13 +2560,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723036363333 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Sum of minted assets (negative on burn) @@ -2626,19 +2578,7 @@ components: description: Metadata key (index) example: "0" json: - type: object - description: A JSON containing details about metadata within transaction - nullable: true - example: - { - "721": - { - "version": 1, - "copyright": "...", - "publisher": ["p...o"], - "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, - }, - } + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -2760,120 +2700,18 @@ components: items: properties: tx_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: - type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs" outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - type: array - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/outputs" tx_metadata: type: array nullable: true items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" metadata: type: object nullable: true @@ -2894,9 +2732,7 @@ components: items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -2917,7 +2753,7 @@ components: type: object properties: policy_id: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_names: type: object properties: @@ -2951,13 +2787,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" total_transactions: type: integer description: Total number of transactions including the given asset @@ -2976,10 +2808,12 @@ components: properties: policy_id: type: string + nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: type: string + nullable: true description: Asset Name (hex) example: 444f4e545350414d asset_name_ascii: @@ -3056,13 +2890,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" minting_txs: type: array description: Array of all mint/burn transactions for an asset @@ -3084,59 +2914,15 @@ components: items: properties: asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" asset_name_ascii: - type: string - description: Asset Name (ASCII) - example: DONTSPAM + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: - type: string - description: The CIP14 fingerprint of the asset - example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_tx_metadata: - type: object - nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: - type: object - description: Asset metadata registered on the Cardano Token Registry - nullable: true - properties: - name: - type: string - example: Rackmob - description: - type: string - example: Metaverse Blockchain Cryptocurrency. - ticker: - type: string - example: MOB - url: - type: string - example: https://www.rackmob.com/ - logo: - type: string - description: A PNG image file as a byte string - example: iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADnmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc - decimals: - type: integer - example: 0 + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" total_supply: type: string example: "35000" diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index c986baf1..67823d90 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1441,25 +1441,15 @@ components: items: properties: hash: - type: string - description: Block Hash in hex - example: 3cc8cfdb2d68fdb2a467292bf0acda7b91ab677741e3e1c1dc111f5be0cef0fe + $ref: "#/components/schemas/blocks/items/properties/hash" epoch_no: - type: integer - description: Epoch number - example: 294 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" abs_slot: - type: integer - description: Absolute Slot number (slots not divided into epochs) - example: 41997413 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" epoch_slot: - type: integer - description: Slot number within Epoch - example: 352613 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" block_no: - type: integer - description: Block Height number on chain - example: 6338276 + $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" genesis: @@ -1620,9 +1610,7 @@ components: description: Pool ID (Hex format) example: a532904ca60e13e88437b58e7c6ff66b8d5e7ec8d3f4b9e4be7820ec active_epoch_no: - type: integer - description: Block number on chain where transaction was included - example: 6354154 + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string description: Pool VRF key hash @@ -1986,7 +1974,7 @@ components: description: The decentralisation parameter (1 fully centralised, 0 fully decentralised) example: 0.1 nullable: true - entropy: + extra_entropy: type: string description: The hash of 32-byte string of extra random-ness added into the protocol's entropy pool example: d982e06fd33e7440b43cefad529b7ecafbaa255e38178ad4189a37e4ce9bf1fa @@ -2072,9 +2060,9 @@ components: description: The maximum number of collateral inputs allowed in a transaction example: 3 nullable: true - coins_per_utxo_word: + coins_per_utxo_size: type: integer - description: The cost per UTxO word + description: The cost per UTxO size example: 34482 nullable: true blocks: @@ -2127,6 +2115,10 @@ components: type: integer description: Counter value of the operational certificate used to create this block example: 8 + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" block_info: type: array items: @@ -2329,7 +2321,7 @@ components: type: object properties: asset_policy: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" quantity: @@ -2366,25 +2358,15 @@ components: description: Hash identifier of the transaction example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e block_hash: - type: string - description: Hash of Block in which transaction was included - example: 90062dfc314c7dc3430922a48f79032a63032206fdca2dfd144cf0930d4aa426 + $ref: "#/components/schemas/blocks/items/properties/hash" block_height: - type: integer - description: Block number on chain where transaction was included - example: 6354154 + $ref: "#/components/schemas/blocks/items/properties/block_height" epoch_no: - type: integer - description: Epoch number - example: 295 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: - type: integer - description: Slot number within epoch - example: 248243 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" absolute_slot: - type: integer - description: Overall slot number (slots from genesis block of chain) - example: 42325043 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" tx_timestamp: type: integer description: UNIX timestamp of the transaction @@ -2418,7 +2400,7 @@ components: description: Slot after which transaction cannot be validated example: 42332172 nullable: true - collaterals: + collateral_inputs: type: array description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) nullable: true @@ -2461,13 +2443,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Asset balance on the selected input transaction @@ -2509,20 +2487,17 @@ components: asset_list: type: array nullable: true - description: An array of assets contained on input UTxO + description: An array of assets on the UTxO items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - description: Asset balance on the selected input transaction + nullable: true + description: Quantity of assets on the UTxO example: 1 outputs: type: array @@ -2559,26 +2534,7 @@ components: description: Total sum on the output address example: 157832856 asset_list: - type: array - nullable: true - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - nullable: true - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - nullable: true - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - nullable: true - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2604,13 +2560,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723036363333 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Sum of minted assets (negative on burn) @@ -2626,19 +2578,7 @@ components: description: Metadata key (index) example: "0" json: - type: object - description: A JSON containing details about metadata within transaction - nullable: true - example: - { - "721": - { - "version": 1, - "copyright": "...", - "publisher": ["p...o"], - "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, - }, - } + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -2760,120 +2700,18 @@ components: items: properties: tx_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: - type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs" outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - type: array - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/outputs" tx_metadata: type: array nullable: true items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" metadata: type: object nullable: true @@ -2894,9 +2732,7 @@ components: items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -2917,7 +2753,7 @@ components: type: object properties: policy_id: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_names: type: object properties: @@ -2951,13 +2787,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" total_transactions: type: integer description: Total number of transactions including the given asset @@ -2976,10 +2808,12 @@ components: properties: policy_id: type: string + nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: type: string + nullable: true description: Asset Name (hex) example: 444f4e545350414d asset_name_ascii: @@ -3056,13 +2890,9 @@ components: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" minting_txs: type: array description: Array of all mint/burn transactions for an asset @@ -3084,59 +2914,15 @@ components: items: properties: asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" asset_name_ascii: - type: string - description: Asset Name (ASCII) - example: DONTSPAM + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: - type: string - description: The CIP14 fingerprint of the asset - example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_tx_metadata: - type: object - nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: - type: object - description: Asset metadata registered on the Cardano Token Registry - nullable: true - properties: - name: - type: string - example: Rackmob - description: - type: string - example: Metaverse Blockchain Cryptocurrency. - ticker: - type: string - example: MOB - url: - type: string - example: https://www.rackmob.com/ - logo: - type: string - description: A PNG image file as a byte string - example: iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADnmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc - decimals: - type: integer - example: 0 + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" total_supply: type: string example: "35000" diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 8ddb036c..90b032bd 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -4,25 +4,15 @@ schemas: items: properties: hash: - type: string - description: Block Hash in hex - example: 3cc8cfdb2d68fdb2a467292bf0acda7b91ab677741e3e1c1dc111f5be0cef0fe + $ref: "#/components/schemas/blocks/items/properties/hash" epoch_no: - type: integer - description: Epoch number - example: 294 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" abs_slot: - type: integer - description: Absolute Slot number (slots not divided into epochs) - example: 41997413 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" epoch_slot: - type: integer - description: Slot number within Epoch - example: 352613 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" block_no: - type: integer - description: Block Height number on chain - example: 6338276 + $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" genesis: @@ -547,7 +537,7 @@ schemas: description: The decentralisation parameter (1 fully centralised, 0 fully decentralised) example: 0.1 nullable: true - entropy: + extra_entropy: type: string description: The hash of 32-byte string of extra random-ness added into the protocol's entropy pool example: d982e06fd33e7440b43cefad529b7ecafbaa255e38178ad4189a37e4ce9bf1fa @@ -633,9 +623,9 @@ schemas: description: The maximum number of collateral inputs allowed in a transaction example: 3 nullable: true - coins_per_utxo_word: + coins_per_utxo_size: type: integer - description: The cost per UTxO word + description: The cost per UTxO size example: 34482 nullable: true blocks: @@ -688,6 +678,10 @@ schemas: type: integer description: Counter value of the operational certificate used to create this block example: 8 + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" block_info: type: array items: @@ -890,7 +884,7 @@ schemas: type: object properties: asset_policy: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" quantity: @@ -927,25 +921,15 @@ schemas: description: Hash identifier of the transaction example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e block_hash: - type: string - description: Hash of Block in which transaction was included - example: 90062dfc314c7dc3430922a48f79032a63032206fdca2dfd144cf0930d4aa426 + $ref: "#/components/schemas/blocks/items/properties/hash" block_height: - type: integer - description: Block number on chain where transaction was included - example: 6354154 + $ref: "#/components/schemas/blocks/items/properties/block_height" epoch_no: - type: integer - description: Epoch number - example: 295 + $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: - type: integer - description: Slot number within epoch - example: 248243 + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" absolute_slot: - type: integer - description: Overall slot number (slots from genesis block of chain) - example: 42325043 + $ref: "#/components/schemas/blocks/items/properties/abs_slot" tx_timestamp: type: integer description: UNIX timestamp of the transaction @@ -979,7 +963,7 @@ schemas: description: Slot after which transaction cannot be validated example: 42332172 nullable: true - collaterals: + collateral_inputs: type: array description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) nullable: true @@ -1022,13 +1006,9 @@ schemas: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Asset balance on the selected input transaction @@ -1070,20 +1050,17 @@ schemas: asset_list: type: array nullable: true - description: An array of assets contained on input UTxO + description: An array of assets on the UTxO items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - description: Asset balance on the selected input transaction + nullable: true + description: Quantity of assets on the UTxO example: 1 outputs: type: array @@ -1120,26 +1097,7 @@ schemas: description: Total sum on the output address example: 157832856 asset_list: - type: array - nullable: true - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - nullable: true - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - nullable: true - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - nullable: true - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -1165,13 +1123,9 @@ schemas: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723036363333 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string description: Sum of minted assets (negative on burn) @@ -1187,19 +1141,7 @@ schemas: description: Metadata key (index) example: "0" json: - type: object - description: A JSON containing details about metadata within transaction - nullable: true - example: - { - "721": - { - "version": 1, - "copyright": "...", - "publisher": ["p...o"], - "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d":{}, - }, - } + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -1321,120 +1263,18 @@ schemas: items: properties: tx_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: - type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/inputs" outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - type: array - description: An array of assets to be included in output UTxO - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb - asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 - quantity: - type: string - description: Sum of assets for output UTxO - example: 1 + $ref: "#/components/schemas/tx_info/items/properties/outputs" tx_metadata: type: array nullable: true items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" metadata: type: object nullable: true @@ -1455,9 +1295,7 @@ schemas: items: properties: tx_hash: - type: string - description: Hash of the transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -1478,7 +1316,7 @@ schemas: type: object properties: policy_id: - $ref: "#/components/schemas/asset_summary/items/properties/policy_id" + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_names: type: object properties: @@ -1512,13 +1350,9 @@ schemas: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: 6cf6b5cf0fefbe9e69d640d8be84912bb2c9e132671954548790bcfb + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 6d65736d6572697a65723038353436 + $ref: "#/components/schemas/asset_info/items/properties/asset_name" total_transactions: type: integer description: Total number of transactions including the given asset @@ -1537,10 +1371,12 @@ schemas: properties: policy_id: type: string + nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: type: string + nullable: true description: Asset Name (hex) example: 444f4e545350414d asset_name_ascii: @@ -1617,13 +1453,9 @@ schemas: items: properties: policy_id: - type: string - description: Asset Policy ID (hex) - example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff + $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" minting_txs: type: array description: Array of all mint/burn transactions for an asset @@ -1645,59 +1477,15 @@ schemas: items: properties: asset_name: - type: string - description: Asset Name (hex) - example: 444f4e545350414d + $ref: "#/components/schemas/asset_info/items/properties/asset_name" asset_name_ascii: - type: string - description: Asset Name (ASCII) - example: DONTSPAM + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: - type: string - description: The CIP14 fingerprint of the asset - example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_tx_metadata: - type: object - nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: - type: object - description: Asset metadata registered on the Cardano Token Registry - nullable: true - properties: - name: - type: string - example: Rackmob - description: - type: string - example: Metaverse Blockchain Cryptocurrency. - ticker: - type: string - example: MOB - url: - type: string - example: https://www.rackmob.com/ - logo: - type: string - description: A PNG image file as a byte string - example: iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADnmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc - decimals: - type: integer - example: 0 + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" total_supply: type: string example: "35000" From 1226f2cda64f953e2cb7b444e6c8338ef5bccd68 Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Fri, 29 Jul 2022 23:14:12 +0100 Subject: [PATCH 04/86] Update asset_history/asset_info spec (#66) --- specs/results/koiosapi-guild.yaml | 37 ++++++++++++++++------------- specs/results/koiosapi-mainnet.yaml | 37 ++++++++++++++++------------- specs/results/koiosapi-testnet.yaml | 37 ++++++++++++++++------------- specs/templates/4-api-schemas.yaml | 37 ++++++++++++++++------------- 4 files changed, 84 insertions(+), 64 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index dcd7292e..67a67e18 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2837,23 +2837,24 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object + type: array nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + items: + properties: + key: + type: string + description: The metadata key + example: "721" + json: + type: object + description: The minting Tx JSON payload if it can be decoded as JSON + example: + { + "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", + "pattern": "08", + "collection": "ccvault.io - the mesmerizer 2021", + "description": "Thanks for supporting ccvault.io development!", + } token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry @@ -2904,10 +2905,14 @@ components: type: string description: Hash of minting/burning transaction example: e1ecc517f95715bb87681cfde2c594dbc971739f84f8bfda16170b35d63d0ddf + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" quantity: type: string description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" + metadata: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index fbca9219..219605f7 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2837,23 +2837,24 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object + type: array nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + items: + properties: + key: + type: string + description: The metadata key + example: "721" + json: + type: object + description: The minting Tx JSON payload if it can be decoded as JSON + example: + { + "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", + "pattern": "08", + "collection": "ccvault.io - the mesmerizer 2021", + "description": "Thanks for supporting ccvault.io development!", + } token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry @@ -2904,10 +2905,14 @@ components: type: string description: Hash of minting/burning transaction example: e1ecc517f95715bb87681cfde2c594dbc971739f84f8bfda16170b35d63d0ddf + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" quantity: type: string description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" + metadata: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 67823d90..1addfc52 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2837,23 +2837,24 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object + type: array nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + items: + properties: + key: + type: string + description: The metadata key + example: "721" + json: + type: object + description: The minting Tx JSON payload if it can be decoded as JSON + example: + { + "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", + "pattern": "08", + "collection": "ccvault.io - the mesmerizer 2021", + "description": "Thanks for supporting ccvault.io development!", + } token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry @@ -2904,10 +2905,14 @@ components: type: string description: Hash of minting/burning transaction example: e1ecc517f95715bb87681cfde2c594dbc971739f84f8bfda16170b35d63d0ddf + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" quantity: type: string description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" + metadata: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 90b032bd..d8e82ae1 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -1400,23 +1400,24 @@ schemas: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object + type: array nullable: true - properties: - key: - type: string - description: The metadata key - example: "721" - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } + items: + properties: + key: + type: string + description: The metadata key + example: "721" + json: + type: object + description: The minting Tx JSON payload if it can be decoded as JSON + example: + { + "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", + "pattern": "08", + "collection": "ccvault.io - the mesmerizer 2021", + "description": "Thanks for supporting ccvault.io development!", + } token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry @@ -1467,10 +1468,14 @@ schemas: type: string description: Hash of minting/burning transaction example: e1ecc517f95715bb87681cfde2c594dbc971739f84f8bfda16170b35d63d0ddf + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" quantity: type: string description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" + metadata: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets From 7d109f39d66982c165b84170d37088261f9d261f Mon Sep 17 00:00:00 2001 From: "Ola [AHLNET]" Date: Sat, 30 Jul 2022 16:20:17 +0200 Subject: [PATCH 05/86] Update tx_info specs (#67) --- specs/results/koiosapi-guild.yaml | 154 +++++++++++----------------- specs/results/koiosapi-mainnet.yaml | 154 +++++++++++----------------- specs/results/koiosapi-testnet.yaml | 154 +++++++++++----------------- specs/templates/4-api-schemas.yaml | 154 +++++++++++----------------- 4 files changed, 240 insertions(+), 376 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 67a67e18..12966f6e 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2401,89 +2401,92 @@ components: example: 42332172 nullable: true collateral_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral inputs needed for smart contracts in case of contract failure + collateral_outputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + reference_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) + inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of UTxO inputs spent in the transaction + outputs: type: array - description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) - nullable: true + description: An array of UTxO outputs created by the transaction items: type: object - nullable: true properties: payment_addr: type: object properties: bech32: type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned + example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw cred: type: string description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string - description: Hash of Transaction for input UTxO + description: Hash of transaction for UTxO example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: type: integer - description: Index of input UTxO on the mentioned address used for input + description: Index of UTxO in the transaction example: 0 value: type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array + description: Total sum of ADA on the UTxO + example: 157832856 + datum_hash: + type: string nullable: true - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 - inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: + description: Hash of datum (if any) connected to UTxO + example: 30c16dd243324cf9d90ffcf211b9e0f2117a7dc28d17e85927dfe2af3328e5c9 + inline_datum: type: object + nullable: true + description: Allows datums to be attached to UTxO (CIP-32) properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: + bytes: type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string + description: Datum (hex) + example: 19029a + value: + type: object + description: Value (json) + example: { "int": 666 } + reference_script: + type: object nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 + description: Allow reference scripts to be used to satisfy script requirements during validation, rather than requiring the spending transaction to do so. (CIP-33) + properties: + hash: + type: string + description: Hash of referenced script + example: 67f33146617a5e61936081db3b2117cbf59bd2123748f58ac9678656 + size: + type: integer + description: Size in bytes + example: 14 + type: + type: string + description: Type of script + example: plutusV1 + bytes: + type: string + description: Script bytes (hex) + example: 4e4d01000033222220051200120011 + value: + type: object + nullable: true + description: Value (json) + example: null asset_list: type: array nullable: true @@ -2496,45 +2499,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - nullable: true description: Quantity of assets on the UTxO example: 1 - outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 219605f7..281ba15c 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2401,89 +2401,92 @@ components: example: 42332172 nullable: true collateral_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral inputs needed for smart contracts in case of contract failure + collateral_outputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + reference_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) + inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of UTxO inputs spent in the transaction + outputs: type: array - description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) - nullable: true + description: An array of UTxO outputs created by the transaction items: type: object - nullable: true properties: payment_addr: type: object properties: bech32: type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned + example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw cred: type: string description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string - description: Hash of Transaction for input UTxO + description: Hash of transaction for UTxO example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: type: integer - description: Index of input UTxO on the mentioned address used for input + description: Index of UTxO in the transaction example: 0 value: type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array + description: Total sum of ADA on the UTxO + example: 157832856 + datum_hash: + type: string nullable: true - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 - inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: + description: Hash of datum (if any) connected to UTxO + example: 30c16dd243324cf9d90ffcf211b9e0f2117a7dc28d17e85927dfe2af3328e5c9 + inline_datum: type: object + nullable: true + description: Allows datums to be attached to UTxO (CIP-32) properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: + bytes: type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string + description: Datum (hex) + example: 19029a + value: + type: object + description: Value (json) + example: { "int": 666 } + reference_script: + type: object nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 + description: Allow reference scripts to be used to satisfy script requirements during validation, rather than requiring the spending transaction to do so. (CIP-33) + properties: + hash: + type: string + description: Hash of referenced script + example: 67f33146617a5e61936081db3b2117cbf59bd2123748f58ac9678656 + size: + type: integer + description: Size in bytes + example: 14 + type: + type: string + description: Type of script + example: plutusV1 + bytes: + type: string + description: Script bytes (hex) + example: 4e4d01000033222220051200120011 + value: + type: object + nullable: true + description: Value (json) + example: null asset_list: type: array nullable: true @@ -2496,45 +2499,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - nullable: true description: Quantity of assets on the UTxO example: 1 - outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 1addfc52..9c82fcc9 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2401,89 +2401,92 @@ components: example: 42332172 nullable: true collateral_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral inputs needed for smart contracts in case of contract failure + collateral_outputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + reference_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) + inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of UTxO inputs spent in the transaction + outputs: type: array - description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) - nullable: true + description: An array of UTxO outputs created by the transaction items: type: object - nullable: true properties: payment_addr: type: object properties: bech32: type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned + example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw cred: type: string description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string - description: Hash of Transaction for input UTxO + description: Hash of transaction for UTxO example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: type: integer - description: Index of input UTxO on the mentioned address used for input + description: Index of UTxO in the transaction example: 0 value: type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array + description: Total sum of ADA on the UTxO + example: 157832856 + datum_hash: + type: string nullable: true - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 - inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: + description: Hash of datum (if any) connected to UTxO + example: 30c16dd243324cf9d90ffcf211b9e0f2117a7dc28d17e85927dfe2af3328e5c9 + inline_datum: type: object + nullable: true + description: Allows datums to be attached to UTxO (CIP-32) properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: + bytes: type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string + description: Datum (hex) + example: 19029a + value: + type: object + description: Value (json) + example: { "int": 666 } + reference_script: + type: object nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 + description: Allow reference scripts to be used to satisfy script requirements during validation, rather than requiring the spending transaction to do so. (CIP-33) + properties: + hash: + type: string + description: Hash of referenced script + example: 67f33146617a5e61936081db3b2117cbf59bd2123748f58ac9678656 + size: + type: integer + description: Size in bytes + example: 14 + type: + type: string + description: Type of script + example: plutusV1 + bytes: + type: string + description: Script bytes (hex) + example: 4e4d01000033222220051200120011 + value: + type: object + nullable: true + description: Value (json) + example: null asset_list: type: array nullable: true @@ -2496,45 +2499,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - nullable: true description: Quantity of assets on the UTxO example: 1 - outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index d8e82ae1..447ef5d6 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -964,89 +964,92 @@ schemas: example: 42332172 nullable: true collateral_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral inputs needed for smart contracts in case of contract failure + collateral_outputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + reference_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) + inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of UTxO inputs spent in the transaction + outputs: type: array - description: An array of collateral inputs needed when dealing with smart contracts (same json schema as inputs) - nullable: true + description: An array of UTxO outputs created by the transaction items: type: object - nullable: true properties: payment_addr: type: object properties: bech32: type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned + example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw cred: type: string description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string - description: Hash of Transaction for input UTxO + description: Hash of transaction for UTxO example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: type: integer - description: Index of input UTxO on the mentioned address used for input + description: Index of UTxO in the transaction example: 0 value: type: string - description: Balance on the selected input transaction - example: 158005617 - asset_list: - type: array + description: Total sum of ADA on the UTxO + example: 157832856 + datum_hash: + type: string nullable: true - description: An array of assets contained on input UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - quantity: - type: string - description: Asset balance on the selected input transaction - example: 1 - inputs: - type: array - description: An array with details about inputs used in a transaction - items: - type: object - properties: - payment_addr: + description: Hash of datum (if any) connected to UTxO + example: 30c16dd243324cf9d90ffcf211b9e0f2117a7dc28d17e85927dfe2af3328e5c9 + inline_datum: type: object + nullable: true + description: Allows datums to be attached to UTxO (CIP-32) properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - cred: + bytes: type: string - description: Payment credential - example: ac9c9e1ad9c0ba563afe7114be2a7dc7b0842e0220a476f58e9f61b0 - stake_addr: - type: string + description: Datum (hex) + example: 19029a + value: + type: object + description: Value (json) + example: { "int": 666 } + reference_script: + type: object nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's input UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of Transaction for input UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of input UTxO on the mentioned address used for input - example: 0 - value: - type: string - description: Balance on the selected input transaction - example: 158005617 + description: Allow reference scripts to be used to satisfy script requirements during validation, rather than requiring the spending transaction to do so. (CIP-33) + properties: + hash: + type: string + description: Hash of referenced script + example: 67f33146617a5e61936081db3b2117cbf59bd2123748f58ac9678656 + size: + type: integer + description: Size in bytes + example: 14 + type: + type: string + description: Type of script + example: plutusV1 + bytes: + type: string + description: Script bytes (hex) + example: 4e4d01000033222220051200120011 + value: + type: object + nullable: true + description: Value (json) + example: null asset_list: type: array nullable: true @@ -1059,45 +1062,8 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/asset_name" quantity: type: string - nullable: true description: Quantity of assets on the UTxO example: 1 - outputs: - type: array - description: An array with details about outputs from the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - type: string - nullable: true - description: A Cardano staking address (reward account, bech32 encoded) for transaction's output UTxO - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - tx_hash: - type: string - description: Hash of this transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of output UTxO - example: 0 - value: - type: string - description: Total sum on the output address - example: 157832856 - asset_list: - $ref: "#/components/schemas/tx_info/items/properties/inputs/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction From f29e5d602f52491428904e0cb36bc53cce7140fc Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Thu, 4 Aug 2022 18:00:47 +1000 Subject: [PATCH 06/86] Changes for cardano-community/guild-operators/1474 (#68) --- specs/results/koiosapi-guild.yaml | 2 +- specs/results/koiosapi-mainnet.yaml | 2 +- specs/results/koiosapi-testnet.yaml | 2 +- specs/templates/api-main.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 12966f6e..e55d7cdf 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -449,7 +449,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Transaction Metadata description: Get metadata information (if any) for given transaction(s) - /tx_metalabels: + /tx_metalabels: #RPC get: tags: - Transactions diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 281ba15c..a6f6090e 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -449,7 +449,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Transaction Metadata description: Get metadata information (if any) for given transaction(s) - /tx_metalabels: + /tx_metalabels: #RPC get: tags: - Transactions diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 9c82fcc9..ba35af7c 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -449,7 +449,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Transaction Metadata description: Get metadata information (if any) for given transaction(s) - /tx_metalabels: + /tx_metalabels: #RPC get: tags: - Transactions diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index 581e200b..3122f47c 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -234,7 +234,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Transaction Metadata description: Get metadata information (if any) for given transaction(s) - /tx_metalabels: + /tx_metalabels: #RPC get: tags: - Transactions From 269de2dfa3b2eda2eb6df50381794c6a2a5258e0 Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Mon, 8 Aug 2022 05:40:40 +0100 Subject: [PATCH 07/86] [SPEC] Make metadata fields nullable for minting txs (#70) * Make metadata fields nullable for minting txs * Make stake_addr nullable --- specs/results/koiosapi-guild.yaml | 6 +++++- specs/results/koiosapi-mainnet.yaml | 6 +++++- specs/results/koiosapi-testnet.yaml | 6 +++++- specs/templates/4-api-schemas.yaml | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index e55d7cdf..0bcd501f 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2430,7 +2430,9 @@ components: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string description: Hash of transaction for UTxO @@ -2811,6 +2813,7 @@ components: type: string description: The metadata key example: "721" + nullable: true json: type: object description: The minting Tx JSON payload if it can be decoded as JSON @@ -2821,6 +2824,7 @@ components: "collection": "ccvault.io - the mesmerizer 2021", "description": "Thanks for supporting ccvault.io development!", } + nullable: true token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index a6f6090e..b7542ffb 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2430,7 +2430,9 @@ components: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string description: Hash of transaction for UTxO @@ -2811,6 +2813,7 @@ components: type: string description: The metadata key example: "721" + nullable: true json: type: object description: The minting Tx JSON payload if it can be decoded as JSON @@ -2821,6 +2824,7 @@ components: "collection": "ccvault.io - the mesmerizer 2021", "description": "Thanks for supporting ccvault.io development!", } + nullable: true token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index ba35af7c..bd56a063 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2430,7 +2430,9 @@ components: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string description: Hash of transaction for UTxO @@ -2811,6 +2813,7 @@ components: type: string description: The metadata key example: "721" + nullable: true json: type: object description: The minting Tx JSON payload if it can be decoded as JSON @@ -2821,6 +2824,7 @@ components: "collection": "ccvault.io - the mesmerizer 2021", "description": "Thanks for supporting ccvault.io development!", } + nullable: true token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 447ef5d6..e74a877f 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -993,7 +993,9 @@ schemas: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" tx_hash: type: string description: Hash of transaction for UTxO @@ -1374,6 +1376,7 @@ schemas: type: string description: The metadata key example: "721" + nullable: true json: type: object description: The minting Tx JSON payload if it can be decoded as JSON @@ -1384,6 +1387,7 @@ schemas: "collection": "ccvault.io - the mesmerizer 2021", "description": "Thanks for supporting ccvault.io development!", } + nullable: true token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry From 7037a9a6b468ad32b7f641069e9660d9dd160463 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Mon, 8 Aug 2022 20:28:51 +1000 Subject: [PATCH 08/86] Bump Specs version to 1.0.6 (#71) --- specs/results/koiosapi-guild.yaml | 2 +- specs/results/koiosapi-mainnet.yaml | 2 +- specs/results/koiosapi-testnet.yaml | 2 +- specs/templates/1-api-info.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 0bcd501f..0d64ce03 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.5 + version: 1.0.6 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index b7542ffb..e74e603b 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.5 + version: 1.0.6 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index bd56a063..dae083a7 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.5 + version: 1.0.6 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 3965e60a..b7e45f33 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -1,6 +1,6 @@ info: title: Koios API - version: 1.0.5 + version: 1.0.6 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. From ff7b790a25c53718e95f96f5853dabaab0e74c36 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Thu, 11 Aug 2022 18:15:56 +1000 Subject: [PATCH 09/86] Update schema as per feedback received (#75) --- specs/results/koiosapi-guild.yaml | 14 +++++++++----- specs/results/koiosapi-mainnet.yaml | 14 +++++++++----- specs/results/koiosapi-testnet.yaml | 14 +++++++++----- specs/templates/4-api-schemas.yaml | 14 +++++++++----- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 0d64ce03..bb43d9b5 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1931,12 +1931,12 @@ components: example: 1100 nullable: true key_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake address example: 2000000 nullable: true pool_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake pool example: 500000000 nullable: true @@ -1990,12 +1990,12 @@ components: example: 0 nullable: true min_utxo_value: - type: integer + type: string description: The minimum value of a UTxO entry example: 34482 nullable: true min_pool_cost: - type: integer + type: string description: The minimum pool cost example: 340000000 nullable: true @@ -2061,7 +2061,7 @@ components: example: 3 nullable: true coins_per_utxo_size: - type: integer + type: string description: The cost per UTxO size example: 34482 nullable: true @@ -2164,6 +2164,10 @@ components: description: Total fees of the block (in lovelace) example: 2346834 nullable: true + num_confirmations: + type: integers + description: Number of confirmations for the block + example: 664275 parent_hash: type: string description: Hash of the parent of this block diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index e74e603b..9d9d835d 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1931,12 +1931,12 @@ components: example: 1100 nullable: true key_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake address example: 2000000 nullable: true pool_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake pool example: 500000000 nullable: true @@ -1990,12 +1990,12 @@ components: example: 0 nullable: true min_utxo_value: - type: integer + type: string description: The minimum value of a UTxO entry example: 34482 nullable: true min_pool_cost: - type: integer + type: string description: The minimum pool cost example: 340000000 nullable: true @@ -2061,7 +2061,7 @@ components: example: 3 nullable: true coins_per_utxo_size: - type: integer + type: string description: The cost per UTxO size example: 34482 nullable: true @@ -2164,6 +2164,10 @@ components: description: Total fees of the block (in lovelace) example: 2346834 nullable: true + num_confirmations: + type: integers + description: Number of confirmations for the block + example: 664275 parent_hash: type: string description: Hash of the parent of this block diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index dae083a7..74ed2fb5 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1931,12 +1931,12 @@ components: example: 1100 nullable: true key_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake address example: 2000000 nullable: true pool_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake pool example: 500000000 nullable: true @@ -1990,12 +1990,12 @@ components: example: 0 nullable: true min_utxo_value: - type: integer + type: string description: The minimum value of a UTxO entry example: 34482 nullable: true min_pool_cost: - type: integer + type: string description: The minimum pool cost example: 340000000 nullable: true @@ -2061,7 +2061,7 @@ components: example: 3 nullable: true coins_per_utxo_size: - type: integer + type: string description: The cost per UTxO size example: 34482 nullable: true @@ -2164,6 +2164,10 @@ components: description: Total fees of the block (in lovelace) example: 2346834 nullable: true + num_confirmations: + type: integers + description: Number of confirmations for the block + example: 664275 parent_hash: type: string description: Hash of the parent of this block diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index e74a877f..278a7d59 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -494,12 +494,12 @@ schemas: example: 1100 nullable: true key_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake address example: 2000000 nullable: true pool_deposit: - type: integer + type: string description: The amount (in lovelace) required for a deposit to register a stake pool example: 500000000 nullable: true @@ -553,12 +553,12 @@ schemas: example: 0 nullable: true min_utxo_value: - type: integer + type: string description: The minimum value of a UTxO entry example: 34482 nullable: true min_pool_cost: - type: integer + type: string description: The minimum pool cost example: 340000000 nullable: true @@ -624,7 +624,7 @@ schemas: example: 3 nullable: true coins_per_utxo_size: - type: integer + type: string description: The cost per UTxO size example: 34482 nullable: true @@ -727,6 +727,10 @@ schemas: description: Total fees of the block (in lovelace) example: 2346834 nullable: true + num_confirmations: + type: integers + description: Number of confirmations for the block + example: 664275 parent_hash: type: string description: Hash of the parent of this block From ffc8943ecb59f5273f64290e9b196ed746ec9bcd Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Sun, 14 Aug 2022 13:25:52 +0100 Subject: [PATCH 10/86] Update specs (#79) --- specs/results/koiosapi-guild.yaml | 6 ++++-- specs/results/koiosapi-mainnet.yaml | 6 ++++-- specs/results/koiosapi-testnet.yaml | 6 ++++-- specs/templates/4-api-schemas.yaml | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index bb43d9b5..3f89998b 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2165,7 +2165,7 @@ components: example: 2346834 nullable: true num_confirmations: - type: integers + type: integer description: Number of confirmations for the block example: 664275 parent_hash: @@ -2507,6 +2507,8 @@ components: type: string description: Quantity of assets on the UTxO example: 1 + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2714,7 +2716,7 @@ components: type: array items: properties: - metalabel: + key: type: string description: A distinct known metalabel example: "721" diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 9d9d835d..9ce9c95e 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2165,7 +2165,7 @@ components: example: 2346834 nullable: true num_confirmations: - type: integers + type: integer description: Number of confirmations for the block example: 664275 parent_hash: @@ -2507,6 +2507,8 @@ components: type: string description: Quantity of assets on the UTxO example: 1 + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2714,7 +2716,7 @@ components: type: array items: properties: - metalabel: + key: type: string description: A distinct known metalabel example: "721" diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 74ed2fb5..1d529e6d 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2165,7 +2165,7 @@ components: example: 2346834 nullable: true num_confirmations: - type: integers + type: integer description: Number of confirmations for the block example: 664275 parent_hash: @@ -2507,6 +2507,8 @@ components: type: string description: Quantity of assets on the UTxO example: 1 + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2714,7 +2716,7 @@ components: type: array items: properties: - metalabel: + key: type: string description: A distinct known metalabel example: "721" diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 278a7d59..6b6aa526 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -728,7 +728,7 @@ schemas: example: 2346834 nullable: true num_confirmations: - type: integers + type: integer description: Number of confirmations for the block example: 664275 parent_hash: @@ -1070,6 +1070,8 @@ schemas: type: string description: Quantity of assets on the UTxO example: 1 + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -1277,7 +1279,7 @@ schemas: type: array items: properties: - metalabel: + key: type: string description: A distinct known metalabel example: "721" From ce6cc9cfb636054d9dc2ec120c914ef3f2f781d2 Mon Sep 17 00:00:00 2001 From: "Ola [AHLNET]" Date: Tue, 16 Aug 2022 09:31:33 +0200 Subject: [PATCH 11/86] Update tx_info schema for collateral_output (#81) * Update tx_info schema for collateral_output * make it nullable --- specs/results/koiosapi-guild.yaml | 7 ++++--- specs/results/koiosapi-mainnet.yaml | 7 ++++--- specs/results/koiosapi-testnet.yaml | 7 ++++--- specs/templates/4-api-schemas.yaml | 7 ++++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 3f89998b..24ac8d4a 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2407,9 +2407,10 @@ components: collateral_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of collateral inputs needed for smart contracts in case of contract failure - collateral_outputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + collateral_output: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items" + description: A collateral output for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + nullable: true reference_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 9ce9c95e..7944e93c 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2407,9 +2407,10 @@ components: collateral_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of collateral inputs needed for smart contracts in case of contract failure - collateral_outputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + collateral_output: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items" + description: A collateral output for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + nullable: true reference_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 1d529e6d..10345cc2 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2407,9 +2407,10 @@ components: collateral_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of collateral inputs needed for smart contracts in case of contract failure - collateral_outputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + collateral_output: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items" + description: A collateral output for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + nullable: true reference_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 6b6aa526..1a4be6e4 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -970,9 +970,10 @@ schemas: collateral_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of collateral inputs needed for smart contracts in case of contract failure - collateral_outputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - description: An array of collateral outputs for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + collateral_output: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items" + description: A collateral output for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + nullable: true reference_inputs: $ref: "#/components/schemas/tx_info/items/properties/outputs" description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) From 8f6a8fd30278a9e2180a3c4eb6eeeac8bb511c8d Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:40:11 +1000 Subject: [PATCH 12/86] Add inline_datum and reference_script to addr_info (#82) --- specs/results/koiosapi-guild.yaml | 4 ++++ specs/results/koiosapi-mainnet.yaml | 4 ++++ specs/results/koiosapi-testnet.yaml | 4 ++++ specs/templates/4-api-schemas.yaml | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 24ac8d4a..7fcc5a2a 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2215,6 +2215,10 @@ components: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 7944e93c..d9381473 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2215,6 +2215,10 @@ components: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 10345cc2..dfde5cab 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2215,6 +2215,10 @@ components: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 1a4be6e4..a1069e1a 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -778,6 +778,10 @@ schemas: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: From f9be2a7101c19e3fa0546be7cac80bc948256d13 Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Tue, 30 Aug 2022 07:07:20 +0100 Subject: [PATCH 13/86] Add sigma field to /pool_info (#88) --- specs/results/koiosapi-guild.yaml | 5 +++++ specs/results/koiosapi-mainnet.yaml | 5 +++++ specs/results/koiosapi-testnet.yaml | 5 +++++ specs/templates/4-api-schemas.yaml | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 7fcc5a2a..0d90aff6 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1722,6 +1722,11 @@ components: nullable: true description: Pool active stake (will be null post epoch transition until dbsync calculation is complete) example: "64328627680963" + sigma: + type: number + nullable: true + description: Pool relative active stake share + example: 0.0034839235 block_count: type: integer nullable: true diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index d9381473..f5d6be4c 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1722,6 +1722,11 @@ components: nullable: true description: Pool active stake (will be null post epoch transition until dbsync calculation is complete) example: "64328627680963" + sigma: + type: number + nullable: true + description: Pool relative active stake share + example: 0.0034839235 block_count: type: integer nullable: true diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index dfde5cab..7511e77e 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1722,6 +1722,11 @@ components: nullable: true description: Pool active stake (will be null post epoch transition until dbsync calculation is complete) example: "64328627680963" + sigma: + type: number + nullable: true + description: Pool relative active stake share + example: 0.0034839235 block_count: type: integer nullable: true diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index a1069e1a..a02aee8b 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -285,6 +285,11 @@ schemas: nullable: true description: Pool active stake (will be null post epoch transition until dbsync calculation is complete) example: "64328627680963" + sigma: + type: number + nullable: true + description: Pool relative active stake share + example: 0.0034839235 block_count: type: integer nullable: true From 1adf23f50c04c853a1cfedbed6a1f5b4faacf5e5 Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Tue, 30 Aug 2022 07:08:09 +0100 Subject: [PATCH 14/86] [SPEC] Bulk RPCs schema update (#87) * Bulk /block_txs * Bulk /address_info schema * Bulk /address_assets schema * Bulk /account_info and simplify requestBodies * Bulk /account_rewards * Bulk /account_updates * Bulk /account_addresses * Bulk /account_assets * Bulk /account_history Co-authored-by: RdLrT <3169068+rdlrt@users.noreply.github.com> --- specs/results/koiosapi-guild.yaml | 294 ++++++++++++++++------- specs/results/koiosapi-mainnet.yaml | 294 ++++++++++++++++------- specs/results/koiosapi-testnet.yaml | 294 ++++++++++++++++------- specs/templates/3-api-requestBodies.yaml | 65 ++++- specs/templates/4-api-schemas.yaml | 156 ++++++++---- specs/templates/api-main.yaml | 77 +++--- specs/templates/example-map.json | 14 +- 7 files changed, 834 insertions(+), 360 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 0d90aff6..257c952b 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -349,7 +349,7 @@ paths: tags: - Block requestBody: - $ref: "#/components/requestBodies/block_info" + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of detailed block information @@ -366,11 +366,11 @@ paths: summary: Block Information description: Get detailed information about a specific block /block_txs: #RPC - get: + post: tags: - Block - parameters: - - $ref: "#/components/parameters/_block_hash" + requestBody: + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of transactions hashes @@ -385,7 +385,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Block Transactions - description: Get a list of all transactions included in a provided block + description: Get a list of all transactions included in provided blocks /tx_info: #RPC post: tags: @@ -520,11 +520,11 @@ paths: summary: Transaction Status (Block Confirmations) description: Get the number of block confirmations for a given transaction hash list /address_info: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address information @@ -539,7 +539,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Information - description: Get address info - balance, associated stake address (if any) and UTxO set + description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses /address_txs: #RPC post: tags: @@ -562,11 +562,11 @@ paths: summary: Address Transactions description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /address_assets: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address_assets" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address-owned assets @@ -581,7 +581,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for a given address + description: Get the list of all the assets (policy, name and quantity) for given addresses /credential_txs: #RPC post: tags: @@ -623,11 +623,11 @@ paths: summary: Account List description: Get a list of all accounts /account_info: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account information @@ -642,14 +642,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Information - description: Get the account info of any (payment or staking) address + description: Get the account information for given stake addresses (accounts) /account_rewards: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" - - $ref: "#/components/parameters/_earned_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of reward history information @@ -665,14 +664,13 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Rewards description: >- - Get the full rewards history (including MIR) for a stake address, or - certain epoch if specified + Get the full rewards history (including MIR) for given stake addresses (accounts) /account_updates: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account updates information @@ -689,13 +687,13 @@ paths: summary: Account Updates description: >- Get the account updates (registration, deregistration, delegation and - withdrawals) + withdrawals) for given stake addresses (accounts) /account_addresses: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of payment addresses @@ -710,13 +708,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Addresses - description: Get all addresses associated with an account + description: Get all addresses associated with given staking accounts /account_assets: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of assets owned by account @@ -731,14 +729,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Assets - description: Get the native asset balance of an account + description: Get the native asset balance of given accounts /account_history: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" - - $ref: "#/components/parameters/_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of active stake values per epoch @@ -753,7 +750,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account History - description: Get the staking history of an account + description: Get the staking history of given stake addresses (accounts) /asset_list: get: tags: @@ -1309,7 +1306,7 @@ components: required: true allowEmptyValue: false requestBodies: - block_info: + block_hashes: content: application/json: schema: @@ -1327,6 +1324,24 @@ components: - af2f6f7dd4e4ea6765103a1e38e023da3edd2b3c7fea2aa367222564dbe01cfd - bddbbc6df0ad09567a513349bafd56d8ec5c8fcd9ee9db12173624b896350d57 - 732bf9bbc3780e6cd7ad57a3889dd5904f6e8a27d54eabf43eb0dbc485323f04 + payment_addresses: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + example: + _addresses: + - addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z + - addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu address_txs: content: application/json: @@ -1350,6 +1365,47 @@ components: - addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z - addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu _after_block_height: 120000 + stake_addresses_with_epoch_no: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _epoch_no: + format: integer + type: integer + description: Only fetch information for a specific epoch + example: + _stake_addresses: + - stake_test1uzljtr5092ad3mhwln29fh8e4hvyaljhy58tf49wq8r4xuquc8zvz + - stake_test1uqhc28rj0ez8xmwmscnyytrhjjxa5y9q9mjm07vv2wra24s2v7e8s + _epoch_no: 17 + stake_addresses: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + example: + _stake_addresses: + - stake_test1uzljtr5092ad3mhwln29fh8e4hvyaljhy58tf49wq8r4xuquc8zvz + - stake_test1uqhc28rj0ez8xmwmscnyytrhjjxa5y9q9mjm07vv2wra24s2v7e8s credential_txs: content: application/json: @@ -2186,13 +2242,19 @@ components: items: type: object properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + tx_hashes: + type: array + items: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" address_info: type: array items: type: object properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2221,9 +2283,9 @@ components: datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" inline_datum: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" reference_script: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: @@ -2240,7 +2302,14 @@ components: block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" address_assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + assets: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -2255,6 +2324,8 @@ components: items: type: object properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" status: type: string description: Stake address status @@ -2295,52 +2366,92 @@ components: items: type: object properties: - earned_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - spendable_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - amount: - type: string - description: Amount of rewards earned (in lovelace) - type: - type: string - description: The source of the rewards - enum: [member, leader, treasury, reserves] - example: member - pool_id: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + rewards: + type: array + items: + type: object + properties: + earned_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + spendable_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + amount: + type: string + description: Amount of rewards earned (in lovelace) + type: + type: string + description: The source of the rewards + enum: [member, leader, treasury, reserves] + example: member + pool_id: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" account_updates: type: array items: type: object properties: - action_type: - type: string - description: Type of certificate submitted - enum: ["registration", "delegation", "withdrawal", "deregistration"] - example: "registration" - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + updates: + type: array + items: + type: object + properties: + action_type: + type: string + description: Type of certificate submitted + enum: ["registration", "delegation", "withdrawal", "deregistration"] + example: "registration" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" account_addresses: type: array items: type: object properties: - address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + addresses: + type: array + items: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" account_assets: type: array items: type: object properties: - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + assets: + type: array + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + assets: + type: array + items: + type: object + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + asset_policy: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + balance: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -2349,18 +2460,23 @@ components: type: string description: Cardano staking address (reward account) in bech32 format example: stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz - pool_id: - type: string - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt - epoch_no: - type: integer - description: Epoch number - example: 301 - active_stake: - type: string - description: Active stake amount (in lovelaces) - example: 682334162 + history: + type: array + items: + type: object + properties: + pool_id: + type: string + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + epoch_no: + type: integer + description: Epoch number + example: 301 + active_stake: + type: string + description: Active stake amount (in lovelaces) + example: 682334162 tx_info: type: array items: diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index f5d6be4c..77969e4a 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -349,7 +349,7 @@ paths: tags: - Block requestBody: - $ref: "#/components/requestBodies/block_info" + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of detailed block information @@ -366,11 +366,11 @@ paths: summary: Block Information description: Get detailed information about a specific block /block_txs: #RPC - get: + post: tags: - Block - parameters: - - $ref: "#/components/parameters/_block_hash" + requestBody: + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of transactions hashes @@ -385,7 +385,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Block Transactions - description: Get a list of all transactions included in a provided block + description: Get a list of all transactions included in provided blocks /tx_info: #RPC post: tags: @@ -520,11 +520,11 @@ paths: summary: Transaction Status (Block Confirmations) description: Get the number of block confirmations for a given transaction hash list /address_info: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address information @@ -539,7 +539,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Information - description: Get address info - balance, associated stake address (if any) and UTxO set + description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses /address_txs: #RPC post: tags: @@ -562,11 +562,11 @@ paths: summary: Address Transactions description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /address_assets: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address_assets" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address-owned assets @@ -581,7 +581,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for a given address + description: Get the list of all the assets (policy, name and quantity) for given addresses /credential_txs: #RPC post: tags: @@ -623,11 +623,11 @@ paths: summary: Account List description: Get a list of all accounts /account_info: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account information @@ -642,14 +642,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Information - description: Get the account info of any (payment or staking) address + description: Get the account information for given stake addresses (accounts) /account_rewards: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" - - $ref: "#/components/parameters/_earned_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of reward history information @@ -665,14 +664,13 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Rewards description: >- - Get the full rewards history (including MIR) for a stake address, or - certain epoch if specified + Get the full rewards history (including MIR) for given stake addresses (accounts) /account_updates: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account updates information @@ -689,13 +687,13 @@ paths: summary: Account Updates description: >- Get the account updates (registration, deregistration, delegation and - withdrawals) + withdrawals) for given stake addresses (accounts) /account_addresses: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of payment addresses @@ -710,13 +708,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Addresses - description: Get all addresses associated with an account + description: Get all addresses associated with given staking accounts /account_assets: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of assets owned by account @@ -731,14 +729,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Assets - description: Get the native asset balance of an account + description: Get the native asset balance of given accounts /account_history: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" - - $ref: "#/components/parameters/_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of active stake values per epoch @@ -753,7 +750,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account History - description: Get the staking history of an account + description: Get the staking history of given stake addresses (accounts) /asset_list: get: tags: @@ -1309,7 +1306,7 @@ components: required: true allowEmptyValue: false requestBodies: - block_info: + block_hashes: content: application/json: schema: @@ -1327,6 +1324,24 @@ components: - fb9087c9f1408a7bbd7b022fd294ab565fec8dd3a8ef091567482722a1fa4e30 - 60188a8dcb6db0d80628815be2cf626c4d17cb3e826cebfca84adaff93ad492a - c6646214a1f377aa461a0163c213fc6b86a559a2d6ebd647d54c4eb00aaab015 + payment_addresses: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + example: + _addresses: + - addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g + - addr1qyfldpcvte8nkfpyv0jdc8e026cz5qedx7tajvupdu2724tlj8sypsq6p90hl40ya97xamkm9fwsppus2ru8zf6j8g9sm578cu address_txs: content: application/json: @@ -1350,6 +1365,47 @@ components: - addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g - addr1qyfldpcvte8nkfpyv0jdc8e026cz5qedx7tajvupdu2724tlj8sypsq6p90hl40ya97xamkm9fwsppus2ru8zf6j8g9sm578cu _after_block_height: 6238675 + stake_addresses_with_epoch_no: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _epoch_no: + format: integer + type: integer + description: Only fetch information for a specific epoch + example: + _stake_addresses: + - stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g + - stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey + _epoch_no: 17 + stake_addresses: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + example: + _stake_addresses: + - stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g + - stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey credential_txs: content: application/json: @@ -2186,13 +2242,19 @@ components: items: type: object properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + tx_hashes: + type: array + items: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" address_info: type: array items: type: object properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2221,9 +2283,9 @@ components: datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" inline_datum: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" reference_script: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: @@ -2240,7 +2302,14 @@ components: block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" address_assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + assets: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -2255,6 +2324,8 @@ components: items: type: object properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" status: type: string description: Stake address status @@ -2295,52 +2366,92 @@ components: items: type: object properties: - earned_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - spendable_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - amount: - type: string - description: Amount of rewards earned (in lovelace) - type: - type: string - description: The source of the rewards - enum: [member, leader, treasury, reserves] - example: member - pool_id: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + rewards: + type: array + items: + type: object + properties: + earned_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + spendable_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + amount: + type: string + description: Amount of rewards earned (in lovelace) + type: + type: string + description: The source of the rewards + enum: [member, leader, treasury, reserves] + example: member + pool_id: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" account_updates: type: array items: type: object properties: - action_type: - type: string - description: Type of certificate submitted - enum: ["registration", "delegation", "withdrawal", "deregistration"] - example: "registration" - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + updates: + type: array + items: + type: object + properties: + action_type: + type: string + description: Type of certificate submitted + enum: ["registration", "delegation", "withdrawal", "deregistration"] + example: "registration" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" account_addresses: type: array items: type: object properties: - address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + addresses: + type: array + items: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" account_assets: type: array items: type: object properties: - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + assets: + type: array + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + assets: + type: array + items: + type: object + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + asset_policy: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + balance: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -2349,18 +2460,23 @@ components: type: string description: Cardano staking address (reward account) in bech32 format example: stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz - pool_id: - type: string - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt - epoch_no: - type: integer - description: Epoch number - example: 301 - active_stake: - type: string - description: Active stake amount (in lovelaces) - example: 682334162 + history: + type: array + items: + type: object + properties: + pool_id: + type: string + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + epoch_no: + type: integer + description: Epoch number + example: 301 + active_stake: + type: string + description: Active stake amount (in lovelaces) + example: 682334162 tx_info: type: array items: diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 7511e77e..1d57f9fc 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -349,7 +349,7 @@ paths: tags: - Block requestBody: - $ref: "#/components/requestBodies/block_info" + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of detailed block information @@ -366,11 +366,11 @@ paths: summary: Block Information description: Get detailed information about a specific block /block_txs: #RPC - get: + post: tags: - Block - parameters: - - $ref: "#/components/parameters/_block_hash" + requestBody: + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of transactions hashes @@ -385,7 +385,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Block Transactions - description: Get a list of all transactions included in a provided block + description: Get a list of all transactions included in provided blocks /tx_info: #RPC post: tags: @@ -520,11 +520,11 @@ paths: summary: Transaction Status (Block Confirmations) description: Get the number of block confirmations for a given transaction hash list /address_info: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address information @@ -539,7 +539,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Information - description: Get address info - balance, associated stake address (if any) and UTxO set + description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses /address_txs: #RPC post: tags: @@ -562,11 +562,11 @@ paths: summary: Address Transactions description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /address_assets: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address_assets" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address-owned assets @@ -581,7 +581,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for a given address + description: Get the list of all the assets (policy, name and quantity) for given addresses /credential_txs: #RPC post: tags: @@ -623,11 +623,11 @@ paths: summary: Account List description: Get a list of all accounts /account_info: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account information @@ -642,14 +642,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Information - description: Get the account info of any (payment or staking) address + description: Get the account information for given stake addresses (accounts) /account_rewards: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" - - $ref: "#/components/parameters/_earned_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of reward history information @@ -665,14 +664,13 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Rewards description: >- - Get the full rewards history (including MIR) for a stake address, or - certain epoch if specified + Get the full rewards history (including MIR) for given stake addresses (accounts) /account_updates: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account updates information @@ -689,13 +687,13 @@ paths: summary: Account Updates description: >- Get the account updates (registration, deregistration, delegation and - withdrawals) + withdrawals) for given stake addresses (accounts) /account_addresses: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of payment addresses @@ -710,13 +708,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Addresses - description: Get all addresses associated with an account + description: Get all addresses associated with given staking accounts /account_assets: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of assets owned by account @@ -731,14 +729,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Assets - description: Get the native asset balance of an account + description: Get the native asset balance of given accounts /account_history: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" - - $ref: "#/components/parameters/_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of active stake values per epoch @@ -753,7 +750,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account History - description: Get the staking history of an account + description: Get the staking history of given stake addresses (accounts) /asset_list: get: tags: @@ -1309,7 +1306,7 @@ components: required: true allowEmptyValue: false requestBodies: - block_info: + block_hashes: content: application/json: schema: @@ -1327,6 +1324,24 @@ components: - f75fea40852ed7d7f539d008e45255725daef8553ae7162750836f279570813a - ff9f0c7fb1136de2cd6f10c9a140af2887f1d3614cc949bfeb262266d4c202b7 - 5ef645ee519cde94a82f0aa880048c37978374f248f11e408ac0571a9054d9d3 + payment_addresses: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + example: + _addresses: + - addr_test1qzx9hu8j4ah3auytk0mwcupd69hpc52t0cw39a65ndrah86djs784u92a3m5w475w3w35tyd6v3qumkze80j8a6h5tuqq5xe8y + - addr_test1qrk7920v35zukhcch4kyydy6rxnhqdcvetkvngeqrvtgavw8tpzdklse3kwer7urhrlfg962m9fc8cznfcdpka5pd07sgf8n0w address_txs: content: application/json: @@ -1350,6 +1365,47 @@ components: - addr_test1qzx9hu8j4ah3auytk0mwcupd69hpc52t0cw39a65ndrah86djs784u92a3m5w475w3w35tyd6v3qumkze80j8a6h5tuqq5xe8y - addr_test1qrk7920v35zukhcch4kyydy6rxnhqdcvetkvngeqrvtgavw8tpzdklse3kwer7urhrlfg962m9fc8cznfcdpka5pd07sgf8n0w _after_block_height: 2342661 + stake_addresses_with_epoch_no: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _epoch_no: + format: integer + type: integer + description: Only fetch information for a specific epoch + example: + _stake_addresses: + - stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj + - stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx + _epoch_no: 17 + stake_addresses: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + example: + _stake_addresses: + - stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj + - stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx credential_txs: content: application/json: @@ -2186,13 +2242,19 @@ components: items: type: object properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + tx_hashes: + type: array + items: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" address_info: type: array items: type: object properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2221,9 +2283,9 @@ components: datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" inline_datum: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" reference_script: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: @@ -2240,7 +2302,14 @@ components: block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" address_assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + assets: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -2255,6 +2324,8 @@ components: items: type: object properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" status: type: string description: Stake address status @@ -2295,52 +2366,92 @@ components: items: type: object properties: - earned_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - spendable_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - amount: - type: string - description: Amount of rewards earned (in lovelace) - type: - type: string - description: The source of the rewards - enum: [member, leader, treasury, reserves] - example: member - pool_id: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + rewards: + type: array + items: + type: object + properties: + earned_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + spendable_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + amount: + type: string + description: Amount of rewards earned (in lovelace) + type: + type: string + description: The source of the rewards + enum: [member, leader, treasury, reserves] + example: member + pool_id: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" account_updates: type: array items: type: object properties: - action_type: - type: string - description: Type of certificate submitted - enum: ["registration", "delegation", "withdrawal", "deregistration"] - example: "registration" - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + updates: + type: array + items: + type: object + properties: + action_type: + type: string + description: Type of certificate submitted + enum: ["registration", "delegation", "withdrawal", "deregistration"] + example: "registration" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" account_addresses: type: array items: type: object properties: - address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + addresses: + type: array + items: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" account_assets: type: array items: type: object properties: - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + assets: + type: array + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + assets: + type: array + items: + type: object + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + asset_policy: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + balance: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -2349,18 +2460,23 @@ components: type: string description: Cardano staking address (reward account) in bech32 format example: stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz - pool_id: - type: string - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt - epoch_no: - type: integer - description: Epoch number - example: 301 - active_stake: - type: string - description: Active stake amount (in lovelaces) - example: 682334162 + history: + type: array + items: + type: object + properties: + pool_id: + type: string + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + epoch_no: + type: integer + description: Epoch number + example: 301 + active_stake: + type: string + description: Active stake amount (in lovelaces) + example: 682334162 tx_info: type: array items: diff --git a/specs/templates/3-api-requestBodies.yaml b/specs/templates/3-api-requestBodies.yaml index 502e4bcb..c44e7972 100644 --- a/specs/templates/3-api-requestBodies.yaml +++ b/specs/templates/3-api-requestBodies.yaml @@ -1,5 +1,5 @@ requestBodies: - block_info: + block_hashes: content: application/json: schema: @@ -17,6 +17,24 @@ requestBodies: - ##block_info1_rb## - ##block_info2_rb## - ##block_info3_rb## + payment_addresses: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + example: + _addresses: + - ##payment_addresses1_rb## + - ##payment_addresses2_rb## address_txs: content: application/json: @@ -37,9 +55,50 @@ requestBodies: description: Only fetch information after specific block height example: _addresses: - - ##address_txs_addresses1_rb## - - ##address_txs_addresses2_rb## + - ##payment_addresses1_rb## + - ##payment_addresses2_rb## _after_block_height: ##address_txs_after_block_height_rb## + stake_addresses_with_epoch_no: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _epoch_no: + format: integer + type: integer + description: Only fetch information for a specific epoch + example: + _stake_addresses: + - ##stake_addresses1_rb## + - ##stake_addresses2_rb## + _epoch_no: 17 + stake_addresses: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + example: + _stake_addresses: + - ##stake_addresses1_rb## + - ##stake_addresses2_rb## credential_txs: content: application/json: diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index a02aee8b..2c5be26f 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -749,13 +749,19 @@ schemas: items: type: object properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + tx_hashes: + type: array + items: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" address_info: type: array items: type: object properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -784,9 +790,9 @@ schemas: datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" inline_datum: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/inline_datum" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" reference_script: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/reference_script" + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" address_txs: @@ -803,7 +809,14 @@ schemas: block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" address_assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + assets: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -818,6 +831,8 @@ schemas: items: type: object properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" status: type: string description: Stake address status @@ -858,52 +873,92 @@ schemas: items: type: object properties: - earned_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - spendable_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - amount: - type: string - description: Amount of rewards earned (in lovelace) - type: - type: string - description: The source of the rewards - enum: [member, leader, treasury, reserves] - example: member - pool_id: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + rewards: + type: array + items: + type: object + properties: + earned_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + spendable_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + amount: + type: string + description: Amount of rewards earned (in lovelace) + type: + type: string + description: The source of the rewards + enum: [member, leader, treasury, reserves] + example: member + pool_id: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" account_updates: type: array items: type: object properties: - action_type: - type: string - description: Type of certificate submitted - enum: ["registration", "delegation", "withdrawal", "deregistration"] - example: "registration" - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + updates: + type: array + items: + type: object + properties: + action_type: + type: string + description: Type of certificate submitted + enum: ["registration", "delegation", "withdrawal", "deregistration"] + example: "registration" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" account_addresses: type: array items: type: object properties: - address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + addresses: + type: array + items: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" account_assets: type: array items: type: object properties: - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + assets: + type: array + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + assets: + type: array + items: + type: object + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + asset_policy: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + balance: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -912,18 +967,23 @@ schemas: type: string description: Cardano staking address (reward account) in bech32 format example: stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz - pool_id: - type: string - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt - epoch_no: - type: integer - description: Epoch number - example: 301 - active_stake: - type: string - description: Active stake amount (in lovelaces) - example: 682334162 + history: + type: array + items: + type: object + properties: + pool_id: + type: string + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + epoch_no: + type: integer + description: Epoch number + example: 301 + active_stake: + type: string + description: Active stake amount (in lovelaces) + example: 682334162 tx_info: type: array items: diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index 3122f47c..63a1f94d 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -134,7 +134,7 @@ paths: tags: - Block requestBody: - $ref: "#/components/requestBodies/block_info" + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of detailed block information @@ -151,11 +151,11 @@ paths: summary: Block Information description: Get detailed information about a specific block /block_txs: #RPC - get: + post: tags: - Block - parameters: - - $ref: "#/components/parameters/_block_hash" + requestBody: + $ref: "#/components/requestBodies/block_hashes" responses: "200": description: Array of transactions hashes @@ -170,7 +170,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Block Transactions - description: Get a list of all transactions included in a provided block + description: Get a list of all transactions included in provided blocks /tx_info: #RPC post: tags: @@ -305,11 +305,11 @@ paths: summary: Transaction Status (Block Confirmations) description: Get the number of block confirmations for a given transaction hash list /address_info: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address information @@ -324,7 +324,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Information - description: Get address info - balance, associated stake address (if any) and UTxO set + description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses /address_txs: #RPC post: tags: @@ -347,11 +347,11 @@ paths: summary: Address Transactions description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /address_assets: #RPC - get: + post: tags: - Address - parameters: - - $ref: "#/components/parameters/_address_assets" + requestBody: + $ref: "#/components/requestBodies/payment_addresses" responses: "200": description: Array of address-owned assets @@ -366,7 +366,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for a given address + description: Get the list of all the assets (policy, name and quantity) for given addresses /credential_txs: #RPC post: tags: @@ -408,11 +408,11 @@ paths: summary: Account List description: Get a list of all accounts /account_info: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account information @@ -427,14 +427,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Information - description: Get the account info of any (payment or staking) address + description: Get the account information for given stake addresses (accounts) /account_rewards: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" - - $ref: "#/components/parameters/_earned_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of reward history information @@ -450,14 +449,13 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Rewards description: >- - Get the full rewards history (including MIR) for a stake address, or - certain epoch if specified + Get the full rewards history (including MIR) for given stake addresses (accounts) /account_updates: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of account updates information @@ -474,13 +472,13 @@ paths: summary: Account Updates description: >- Get the account updates (registration, deregistration, delegation and - withdrawals) + withdrawals) for given stake addresses (accounts) /account_addresses: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of payment addresses @@ -495,13 +493,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Addresses - description: Get all addresses associated with an account + description: Get all addresses associated with given staking accounts /account_assets: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of assets owned by account @@ -516,14 +514,13 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account Assets - description: Get the native asset balance of an account + description: Get the native asset balance of given accounts /account_history: #RPC - get: + post: tags: - Account - parameters: - - $ref: "#/components/parameters/_any_address" - - $ref: "#/components/parameters/_epoch_no" + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" responses: "200": description: Array of active stake values per epoch @@ -538,7 +535,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Account History - description: Get the staking history of an account + description: Get the staking history of given stake addresses (accounts) /asset_list: get: tags: diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index ef820ae7..71dbeadb 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -62,12 +62,22 @@ } }, "requestBodies": { - "address_txs_addresses1": { + "stake_addresses1": { + "m": "stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g", + "t": "stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj", + "g": "stake_test1uzljtr5092ad3mhwln29fh8e4hvyaljhy58tf49wq8r4xuquc8zvz" + }, + "stake_addresses2": { + "m": "stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey", + "t": "stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx", + "g": "stake_test1uqhc28rj0ez8xmwmscnyytrhjjxa5y9q9mjm07vv2wra24s2v7e8s" + }, + "payment_addresses1": { "m": "addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g", "t": "addr_test1qzx9hu8j4ah3auytk0mwcupd69hpc52t0cw39a65ndrah86djs784u92a3m5w475w3w35tyd6v3qumkze80j8a6h5tuqq5xe8y", "g": "addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z" }, - "address_txs_addresses2": { + "payment_addresses2": { "m": "addr1qyfldpcvte8nkfpyv0jdc8e026cz5qedx7tajvupdu2724tlj8sypsq6p90hl40ya97xamkm9fwsppus2ru8zf6j8g9sm578cu", "t": "addr_test1qrk7920v35zukhcch4kyydy6rxnhqdcvetkvngeqrvtgavw8tpzdklse3kwer7urhrlfg962m9fc8cznfcdpka5pd07sgf8n0w", "g": "addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu" From d3306b03631ea33ce5716bbf72a265ab73c180d3 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Fri, 23 Sep 2022 16:17:06 +1000 Subject: [PATCH 15/86] Bump Koios version to 1.0.7 (#83) ## Description Bump to Koios v1.0.7. To-Do: - [x] - Complete tasks for cardano-community/guild-operators#1501 - [x] - Add entries for `pool_stake_snapshot` and `pool_delegators_history` - [x] - Update entry for `pool_delegator_history` - [x] - Bump version in specs - [x] - Start testing update on guildnet - [x] - Prepare release notes and announcements --- specs/results/koiosapi-guild.yaml | 168 +++++++++++++++++++++-- specs/results/koiosapi-mainnet.yaml | 160 +++++++++++++++++++-- specs/results/koiosapi-testnet.yaml | 160 +++++++++++++++++++-- specs/templates/1-api-info.yaml | 2 +- specs/templates/2-api-params.yaml | 10 ++ specs/templates/3-api-requestBodies.yaml | 2 +- specs/templates/4-api-schemas.yaml | 67 ++++++++- specs/templates/api-main.yaml | 79 ++++++++++- specs/templates/example-map.json | 9 +- 9 files changed, 607 insertions(+), 50 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 257c952b..5f6c47ef 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.6 + version: 1.0.7 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -325,6 +325,28 @@ paths: description: >- Get the protocol parameters for specific epoch, returns information about all epochs if no epoch specified + /epoch_block_protocols: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of distinct block protocol versions counts in epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_params" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Block Protocols + description: >- + Get the information about block protocol distribution in epoch /blocks: get: tags: @@ -627,7 +649,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account information @@ -670,7 +692,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account updates information @@ -693,7 +715,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of payment addresses @@ -714,7 +736,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of assets owned by account @@ -886,6 +908,7 @@ paths: parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" responses: "200": description: Array of Tx hashes that included the given asset @@ -941,13 +964,33 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Information description: Current pool statuses and details for a specified list of pool ids + /pool_stake_snapshot: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool stake information for 3 snapshots + content: + application/json: + schema: + $ref: "#/components/schemas/pool_snapshot" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake Snapshot + description: Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation /pool_delegators: #RPC get: tags: - Pool parameters: - $ref: "#/components/parameters/_pool_bech32" - - $ref: "#/components/parameters/_epoch_no" responses: "200": description: Array of pool delegator information @@ -962,7 +1005,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators List - description: Return information about delegators by a given pool and optional epoch (current if omitted) + description: Return information about live delegators for a given pool. + /pool_delegators_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators History + description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. /pool_blocks: #RPC get: tags: @@ -985,7 +1050,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Blocks description: >- - Return information about blocks minted by a given pool in current epoch + Return information about blocks minted by a given pool for all epochs (or _epoch_no if provided) /pool_history: #RPC get: @@ -1195,6 +1260,16 @@ components: in: query required: true allowEmptyValue: false + _after_block_height: + deprecated: false + name: _after_block_height + description: Block height for specifying time delta + schema: + type: integer + example: 63487 + in: query + required: false + allowEmptyValue: true _epoch_no: deprecated: false name: _epoch_no @@ -1385,9 +1460,9 @@ components: description: Only fetch information for a specific epoch example: _stake_addresses: - - stake_test1uzljtr5092ad3mhwln29fh8e4hvyaljhy58tf49wq8r4xuquc8zvz - - stake_test1uqhc28rj0ez8xmwmscnyytrhjjxa5y9q9mjm07vv2wra24s2v7e8s - _epoch_no: 17 + - stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2 + - stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd + _epoch_no: 1500 stake_addresses: content: application/json: @@ -1404,8 +1479,8 @@ components: description: Array of Cardano stake address(es) in bech32 format example: _stake_addresses: - - stake_test1uzljtr5092ad3mhwln29fh8e4hvyaljhy58tf49wq8r4xuquc8zvz - - stake_test1uqhc28rj0ez8xmwmscnyytrhjjxa5y9q9mjm07vv2wra24s2v7e8s + - stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2 + - stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd credential_txs: content: application/json: @@ -1807,6 +1882,30 @@ components: nullable: true description: Pool live saturation (decimal format) example: 94.52 + pool_snapshot: + type: array + nullable: true + items: + type: object + properties: + snapshot: + type: string + description: Type of snapshot ("Mark", "Set" or "Go") + example: "Mark" + epoch_no: + type: integer + description: Epoch number for the snapshot entry + example: 324 + nonce: + $ref: "#/components/schemas/epoch_params/items/properties/nonce" + pool_stake: + type: string + description: Pool's Active Stake for the given epoch + example: "100000000000" + active_stake: + type: string + description: Total Active Stake for the given epoch + example: "103703246364020" pool_delegators: type: array nullable: true @@ -1823,6 +1922,24 @@ components: type: integer description: Epoch number in which the delegation becomes active example: 324 + latest_delegation_hash: + type: string + description: Latest transaction hash used for delegation by the account + example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_delegators_history: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + epoch_no: + type: integer + description: Epoch number for the delegation history + example: 324 pool_blocks: type: array nullable: true @@ -1876,6 +1993,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_url" meta_hash: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" pool_status: $ref: "#/components/schemas/pool_info/items/properties/pool_status" retiring_epoch: @@ -2126,6 +2245,22 @@ components: description: The cost per UTxO size example: 34482 nullable: true + epoch_block_protocols: + type: array + items: + properties: + proto_major: + type: integer + description: Protocol major version + example: 6 + proto_minor: + type: integer + description: Protocol major version + example: 2 + blocks: + type: integer + description: Amount of blocks with specified major and protocol combination + example: 2183 blocks: type: array items: @@ -2260,7 +2395,9 @@ components: description: Sum of all UTxO values beloning to address example: 10723473983 stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" script_address: type: boolean description: Signifies whether the address is a script address @@ -2937,7 +3074,8 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: array + type: object + description: Transaction metadata of the latest asset minting transaction nullable: true items: properties: diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 77969e4a..cb3b6b3e 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.6 + version: 1.0.7 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -325,6 +325,28 @@ paths: description: >- Get the protocol parameters for specific epoch, returns information about all epochs if no epoch specified + /epoch_block_protocols: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of distinct block protocol versions counts in epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_params" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Block Protocols + description: >- + Get the information about block protocol distribution in epoch /blocks: get: tags: @@ -627,7 +649,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account information @@ -670,7 +692,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account updates information @@ -693,7 +715,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of payment addresses @@ -714,7 +736,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of assets owned by account @@ -886,6 +908,7 @@ paths: parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" responses: "200": description: Array of Tx hashes that included the given asset @@ -941,13 +964,33 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Information description: Current pool statuses and details for a specified list of pool ids + /pool_stake_snapshot: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool stake information for 3 snapshots + content: + application/json: + schema: + $ref: "#/components/schemas/pool_snapshot" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake Snapshot + description: Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation /pool_delegators: #RPC get: tags: - Pool parameters: - $ref: "#/components/parameters/_pool_bech32" - - $ref: "#/components/parameters/_epoch_no" responses: "200": description: Array of pool delegator information @@ -962,7 +1005,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators List - description: Return information about delegators by a given pool and optional epoch (current if omitted) + description: Return information about live delegators for a given pool. + /pool_delegators_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators History + description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. /pool_blocks: #RPC get: tags: @@ -985,7 +1050,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Blocks description: >- - Return information about blocks minted by a given pool in current epoch + Return information about blocks minted by a given pool for all epochs (or _epoch_no if provided) /pool_history: #RPC get: @@ -1195,6 +1260,16 @@ components: in: query required: true allowEmptyValue: false + _after_block_height: + deprecated: false + name: _after_block_height + description: Block height for specifying time delta + schema: + type: integer + example: 63487 + in: query + required: false + allowEmptyValue: true _epoch_no: deprecated: false name: _epoch_no @@ -1387,7 +1462,7 @@ components: _stake_addresses: - stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g - stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey - _epoch_no: 17 + _epoch_no: 350 stake_addresses: content: application/json: @@ -1807,6 +1882,30 @@ components: nullable: true description: Pool live saturation (decimal format) example: 94.52 + pool_snapshot: + type: array + nullable: true + items: + type: object + properties: + snapshot: + type: string + description: Type of snapshot ("Mark", "Set" or "Go") + example: "Mark" + epoch_no: + type: integer + description: Epoch number for the snapshot entry + example: 324 + nonce: + $ref: "#/components/schemas/epoch_params/items/properties/nonce" + pool_stake: + type: string + description: Pool's Active Stake for the given epoch + example: "100000000000" + active_stake: + type: string + description: Total Active Stake for the given epoch + example: "103703246364020" pool_delegators: type: array nullable: true @@ -1823,6 +1922,24 @@ components: type: integer description: Epoch number in which the delegation becomes active example: 324 + latest_delegation_hash: + type: string + description: Latest transaction hash used for delegation by the account + example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_delegators_history: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + epoch_no: + type: integer + description: Epoch number for the delegation history + example: 324 pool_blocks: type: array nullable: true @@ -1876,6 +1993,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_url" meta_hash: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" pool_status: $ref: "#/components/schemas/pool_info/items/properties/pool_status" retiring_epoch: @@ -2126,6 +2245,22 @@ components: description: The cost per UTxO size example: 34482 nullable: true + epoch_block_protocols: + type: array + items: + properties: + proto_major: + type: integer + description: Protocol major version + example: 6 + proto_minor: + type: integer + description: Protocol major version + example: 2 + blocks: + type: integer + description: Amount of blocks with specified major and protocol combination + example: 2183 blocks: type: array items: @@ -2260,7 +2395,9 @@ components: description: Sum of all UTxO values beloning to address example: 10723473983 stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" script_address: type: boolean description: Signifies whether the address is a script address @@ -2937,7 +3074,8 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: array + type: object + description: Transaction metadata of the latest asset minting transaction nullable: true items: properties: diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 1d57f9fc..8cbddb19 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.6 + version: 1.0.7 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -325,6 +325,28 @@ paths: description: >- Get the protocol parameters for specific epoch, returns information about all epochs if no epoch specified + /epoch_block_protocols: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of distinct block protocol versions counts in epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_params" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Block Protocols + description: >- + Get the information about block protocol distribution in epoch /blocks: get: tags: @@ -627,7 +649,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account information @@ -670,7 +692,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account updates information @@ -693,7 +715,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of payment addresses @@ -714,7 +736,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of assets owned by account @@ -886,6 +908,7 @@ paths: parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" responses: "200": description: Array of Tx hashes that included the given asset @@ -941,13 +964,33 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Information description: Current pool statuses and details for a specified list of pool ids + /pool_stake_snapshot: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool stake information for 3 snapshots + content: + application/json: + schema: + $ref: "#/components/schemas/pool_snapshot" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake Snapshot + description: Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation /pool_delegators: #RPC get: tags: - Pool parameters: - $ref: "#/components/parameters/_pool_bech32" - - $ref: "#/components/parameters/_epoch_no" responses: "200": description: Array of pool delegator information @@ -962,7 +1005,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators List - description: Return information about delegators by a given pool and optional epoch (current if omitted) + description: Return information about live delegators for a given pool. + /pool_delegators_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators History + description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. /pool_blocks: #RPC get: tags: @@ -985,7 +1050,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Blocks description: >- - Return information about blocks minted by a given pool in current epoch + Return information about blocks minted by a given pool for all epochs (or _epoch_no if provided) /pool_history: #RPC get: @@ -1195,6 +1260,16 @@ components: in: query required: true allowEmptyValue: false + _after_block_height: + deprecated: false + name: _after_block_height + description: Block height for specifying time delta + schema: + type: integer + example: 63487 + in: query + required: false + allowEmptyValue: true _epoch_no: deprecated: false name: _epoch_no @@ -1387,7 +1462,7 @@ components: _stake_addresses: - stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj - stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx - _epoch_no: 17 + _epoch_no: 200 stake_addresses: content: application/json: @@ -1807,6 +1882,30 @@ components: nullable: true description: Pool live saturation (decimal format) example: 94.52 + pool_snapshot: + type: array + nullable: true + items: + type: object + properties: + snapshot: + type: string + description: Type of snapshot ("Mark", "Set" or "Go") + example: "Mark" + epoch_no: + type: integer + description: Epoch number for the snapshot entry + example: 324 + nonce: + $ref: "#/components/schemas/epoch_params/items/properties/nonce" + pool_stake: + type: string + description: Pool's Active Stake for the given epoch + example: "100000000000" + active_stake: + type: string + description: Total Active Stake for the given epoch + example: "103703246364020" pool_delegators: type: array nullable: true @@ -1823,6 +1922,24 @@ components: type: integer description: Epoch number in which the delegation becomes active example: 324 + latest_delegation_hash: + type: string + description: Latest transaction hash used for delegation by the account + example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_delegators_history: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + epoch_no: + type: integer + description: Epoch number for the delegation history + example: 324 pool_blocks: type: array nullable: true @@ -1876,6 +1993,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_url" meta_hash: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" pool_status: $ref: "#/components/schemas/pool_info/items/properties/pool_status" retiring_epoch: @@ -2126,6 +2245,22 @@ components: description: The cost per UTxO size example: 34482 nullable: true + epoch_block_protocols: + type: array + items: + properties: + proto_major: + type: integer + description: Protocol major version + example: 6 + proto_minor: + type: integer + description: Protocol major version + example: 2 + blocks: + type: integer + description: Amount of blocks with specified major and protocol combination + example: 2183 blocks: type: array items: @@ -2260,7 +2395,9 @@ components: description: Sum of all UTxO values beloning to address example: 10723473983 stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" script_address: type: boolean description: Signifies whether the address is a script address @@ -2937,7 +3074,8 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: array + type: object + description: Transaction metadata of the latest asset minting transaction nullable: true items: properties: diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index b7e45f33..7a5c7f23 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -1,6 +1,6 @@ info: title: Koios API - version: 1.0.6 + version: 1.0.7 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/templates/2-api-params.yaml b/specs/templates/2-api-params.yaml index 742dbe60..01f4abfb 100644 --- a/specs/templates/2-api-params.yaml +++ b/specs/templates/2-api-params.yaml @@ -60,6 +60,16 @@ parameters: in: query required: true allowEmptyValue: false + _after_block_height: + deprecated: false + name: _after_block_height + description: Block height for specifying time delta + schema: + type: integer + example: 63487 + in: query + required: false + allowEmptyValue: true _epoch_no: deprecated: false name: _epoch_no diff --git a/specs/templates/3-api-requestBodies.yaml b/specs/templates/3-api-requestBodies.yaml index c44e7972..50894d07 100644 --- a/specs/templates/3-api-requestBodies.yaml +++ b/specs/templates/3-api-requestBodies.yaml @@ -80,7 +80,7 @@ requestBodies: _stake_addresses: - ##stake_addresses1_rb## - ##stake_addresses2_rb## - _epoch_no: 17 + _epoch_no: ##epoch_no_rb## stake_addresses: content: application/json: diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 2c5be26f..539fa136 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -314,6 +314,30 @@ schemas: nullable: true description: Pool live saturation (decimal format) example: 94.52 + pool_snapshot: + type: array + nullable: true + items: + type: object + properties: + snapshot: + type: string + description: Type of snapshot ("Mark", "Set" or "Go") + example: "Mark" + epoch_no: + type: integer + description: Epoch number for the snapshot entry + example: 324 + nonce: + $ref: "#/components/schemas/epoch_params/items/properties/nonce" + pool_stake: + type: string + description: Pool's Active Stake for the given epoch + example: "100000000000" + active_stake: + type: string + description: Total Active Stake for the given epoch + example: "103703246364020" pool_delegators: type: array nullable: true @@ -330,6 +354,24 @@ schemas: type: integer description: Epoch number in which the delegation becomes active example: 324 + latest_delegation_hash: + type: string + description: Latest transaction hash used for delegation by the account + example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_delegators_history: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + epoch_no: + type: integer + description: Epoch number for the delegation history + example: 324 pool_blocks: type: array nullable: true @@ -383,6 +425,8 @@ schemas: $ref: "#/components/schemas/pool_info/items/properties/meta_url" meta_hash: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" pool_status: $ref: "#/components/schemas/pool_info/items/properties/pool_status" retiring_epoch: @@ -633,6 +677,22 @@ schemas: description: The cost per UTxO size example: 34482 nullable: true + epoch_block_protocols: + type: array + items: + properties: + proto_major: + type: integer + description: Protocol major version + example: 6 + proto_minor: + type: integer + description: Protocol major version + example: 2 + blocks: + type: integer + description: Amount of blocks with specified major and protocol combination + example: 2183 blocks: type: array items: @@ -767,7 +827,9 @@ schemas: description: Sum of all UTxO values beloning to address example: 10723473983 stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" script_address: type: boolean description: Signifies whether the address is a script address @@ -1444,7 +1506,8 @@ schemas: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: array + type: object + description: Transaction metadata of the latest asset minting transaction nullable: true items: properties: diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index 63a1f94d..990c409d 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -110,6 +110,28 @@ paths: description: >- Get the protocol parameters for specific epoch, returns information about all epochs if no epoch specified + /epoch_block_protocols: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of distinct block protocol versions counts in epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_params" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Block Protocols + description: >- + Get the information about block protocol distribution in epoch /blocks: get: tags: @@ -412,7 +434,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account information @@ -455,7 +477,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of account updates information @@ -478,7 +500,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of payment addresses @@ -499,7 +521,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + $ref: "#/components/requestBodies/stake_addresses" responses: "200": description: Array of assets owned by account @@ -671,6 +693,7 @@ paths: parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" responses: "200": description: Array of Tx hashes that included the given asset @@ -726,13 +749,33 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Information description: Current pool statuses and details for a specified list of pool ids + /pool_stake_snapshot: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool stake information for 3 snapshots + content: + application/json: + schema: + $ref: "#/components/schemas/pool_snapshot" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake Snapshot + description: Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation /pool_delegators: #RPC get: tags: - Pool parameters: - $ref: "#/components/parameters/_pool_bech32" - - $ref: "#/components/parameters/_epoch_no" responses: "200": description: Array of pool delegator information @@ -747,7 +790,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators List - description: Return information about delegators by a given pool and optional epoch (current if omitted) + description: Return information about live delegators for a given pool. + /pool_delegators_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators History + description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. /pool_blocks: #RPC get: tags: @@ -770,7 +835,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Blocks description: >- - Return information about blocks minted by a given pool in current epoch + Return information about blocks minted by a given pool for all epochs (or _epoch_no if provided) /pool_history: #RPC get: diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index 71dbeadb..58acc437 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -62,15 +62,20 @@ } }, "requestBodies": { + "epoch_no": { + "m": 350, + "t": 200, + "g": 1500 + }, "stake_addresses1": { "m": "stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g", "t": "stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj", - "g": "stake_test1uzljtr5092ad3mhwln29fh8e4hvyaljhy58tf49wq8r4xuquc8zvz" + "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2" }, "stake_addresses2": { "m": "stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey", "t": "stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx", - "g": "stake_test1uqhc28rj0ez8xmwmscnyytrhjjxa5y9q9mjm07vv2wra24s2v7e8s" + "g": "stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd" }, "payment_addresses1": { "m": "addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g", From fd8c6ef3b6ad670ca6a3cf3dda08e6c6ea737532 Mon Sep 17 00:00:00 2001 From: chadle-git <60274638+chadle-git@users.noreply.github.com> Date: Thu, 29 Sep 2022 22:59:24 -0500 Subject: [PATCH 16/86] Add TosiDrop (#96) * Add TosiDrop * ran create specs to add tosidrop Co-authored-by: chadle-git --- specs/results/koiosapi-guild.yaml | 1 + specs/results/koiosapi-mainnet.yaml | 1 + specs/results/koiosapi-testnet.yaml | 1 + specs/templates/1-api-info.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 5f6c47ef..aa30fb8c 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -193,6 +193,7 @@ info: - [Dandelion](https://dandelion.link) - [Eternl](https://eternl.io/) - [PoolPeek](https://poolpeek.com) + - [TosiDrop](https://tosidrop.io) # FAQ diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index cb3b6b3e..abde5438 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -193,6 +193,7 @@ info: - [Dandelion](https://dandelion.link) - [Eternl](https://eternl.io/) - [PoolPeek](https://poolpeek.com) + - [TosiDrop](https://tosidrop.io) # FAQ diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 8cbddb19..b6d4098e 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -193,6 +193,7 @@ info: - [Dandelion](https://dandelion.link) - [Eternl](https://eternl.io/) - [PoolPeek](https://poolpeek.com) + - [TosiDrop](https://tosidrop.io) # FAQ diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 7a5c7f23..007b90b7 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -192,6 +192,7 @@ info: - [Dandelion](https://dandelion.link) - [Eternl](https://eternl.io/) - [PoolPeek](https://poolpeek.com) + - [TosiDrop](https://tosidrop.io) # FAQ From 30fd565505307fc168af8ff686d6af0bcea609b4 Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Tue, 4 Oct 2022 06:08:21 +0100 Subject: [PATCH 17/86] [SPEC] Align metadata schema between tx_info and tx_metadata (#104) * Align metadata schema between tx_info and tx_metadata * Switch metadata schema * /asset_info metadata fix * Fix reference schema for epoch_block_protocols --- specs/results/koiosapi-guild.yaml | 36 +++-------------------------- specs/results/koiosapi-mainnet.yaml | 36 +++-------------------------- specs/results/koiosapi-testnet.yaml | 36 +++-------------------------- specs/templates/4-api-schemas.yaml | 34 ++------------------------- specs/templates/api-main.yaml | 2 +- 5 files changed, 12 insertions(+), 132 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index aa30fb8c..f1e806fb 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -338,7 +338,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/epoch_params" + $ref: "#/components/schemas/epoch_block_protocols" "400": $ref: "#/components/responses/BadRequest" "401": @@ -2806,17 +2806,7 @@ components: description: Sum of minted assets (negative on burn) example: 1 metadata: - type: array - nullable: true - description: Metadata present with-in a transaction (if any) - items: - properties: - key: - type: string - description: Metadata key (index) - example: "0" - json: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -3075,27 +3065,7 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object - description: Transaction metadata of the latest asset minting transaction - nullable: true - items: - properties: - key: - type: string - description: The metadata key - example: "721" - nullable: true - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } - nullable: true + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index abde5438..33aad972 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -338,7 +338,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/epoch_params" + $ref: "#/components/schemas/epoch_block_protocols" "400": $ref: "#/components/responses/BadRequest" "401": @@ -2806,17 +2806,7 @@ components: description: Sum of minted assets (negative on burn) example: 1 metadata: - type: array - nullable: true - description: Metadata present with-in a transaction (if any) - items: - properties: - key: - type: string - description: Metadata key (index) - example: "0" - json: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -3075,27 +3065,7 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object - description: Transaction metadata of the latest asset minting transaction - nullable: true - items: - properties: - key: - type: string - description: The metadata key - example: "721" - nullable: true - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } - nullable: true + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index b6d4098e..139f9715 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -338,7 +338,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/epoch_params" + $ref: "#/components/schemas/epoch_block_protocols" "400": $ref: "#/components/responses/BadRequest" "401": @@ -2806,17 +2806,7 @@ components: description: Sum of minted assets (negative on burn) example: 1 metadata: - type: array - nullable: true - description: Metadata present with-in a transaction (if any) - items: - properties: - key: - type: string - description: Metadata key (index) - example: "0" - json: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -3075,27 +3065,7 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object - description: Transaction metadata of the latest asset minting transaction - nullable: true - items: - properties: - key: - type: string - description: The metadata key - example: "721" - nullable: true - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } - nullable: true + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 539fa136..1d318afd 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -1237,17 +1237,7 @@ schemas: description: Sum of minted assets (negative on burn) example: 1 metadata: - type: array - nullable: true - description: Metadata present with-in a transaction (if any) - items: - properties: - key: - type: string - description: Metadata key (index) - example: "0" - json: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" certificates: type: array nullable: true @@ -1506,27 +1496,7 @@ schemas: description: Count of total burn transactions example: 5 minting_tx_metadata: - type: object - description: Transaction metadata of the latest asset minting transaction - nullable: true - items: - properties: - key: - type: string - description: The metadata key - example: "721" - nullable: true - json: - type: object - description: The minting Tx JSON payload if it can be decoded as JSON - example: - { - "image": "ipfs://QmVHpTQL4Furw6fFNhSHdT3AWGbDE9Vs341JHJhQF1bAEA", - "pattern": "08", - "collection": "ccvault.io - the mesmerizer 2021", - "description": "Thanks for supporting ccvault.io development!", - } - nullable: true + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index 990c409d..0c4b718e 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -122,7 +122,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/epoch_params" + $ref: "#/components/schemas/epoch_block_protocols" "400": $ref: "#/components/responses/BadRequest" "401": From 80936ee5ecd613af745f75cab5e614446a168c62 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Thu, 6 Oct 2022 23:15:41 +1100 Subject: [PATCH 18/86] Koios 1.0.8 (#106) --- specs/results/koiosapi-guild.yaml | 4 ++-- specs/results/koiosapi-mainnet.yaml | 16 ++++++++-------- specs/results/koiosapi-testnet.yaml | 4 ++-- specs/templates/1-api-info.yaml | 2 +- specs/templates/api-main.yaml | 2 +- specs/templates/example-map.json | 8 ++++---- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index f1e806fb..030cd923 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.7 + version: 1.0.8 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -1028,7 +1028,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators History - description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. + description: Return information about active delegators (incl. history) for a given pool and epoch number (all epochs if not specified). /pool_blocks: #RPC get: tags: diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 33aad972..cbe3506b 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.7 + version: 1.0.8 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -1028,7 +1028,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators History - description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. + description: Return information about active delegators (incl. history) for a given pool and epoch number (all epochs if not specified). /pool_blocks: #RPC get: tags: @@ -1337,7 +1337,7 @@ components: description: Asset Policy ID in hexadecimal format (hex) schema: type: string - example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff + example: 750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501 in: query required: true allowEmptyValue: false @@ -1347,7 +1347,7 @@ components: description: Asset Name in hexadecimal format (hex) schema: type: string - example: 444f4e545350414d + example: 424f4f4b in: query required: false allowEmptyValue: true @@ -1461,8 +1461,8 @@ components: description: Only fetch information for a specific epoch example: _stake_addresses: - - stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g - - stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey + - stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250 + - stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy _epoch_no: 350 stake_addresses: content: @@ -1480,8 +1480,8 @@ components: description: Array of Cardano stake address(es) in bech32 format example: _stake_addresses: - - stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g - - stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey + - stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250 + - stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy credential_txs: content: application/json: diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 139f9715..5dbf0c55 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.7 + version: 1.0.8 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -1028,7 +1028,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators History - description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. + description: Return information about active delegators (incl. history) for a given pool and epoch number (all epochs if not specified). /pool_blocks: #RPC get: tags: diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 007b90b7..b0678013 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -1,6 +1,6 @@ info: title: Koios API - version: 1.0.7 + version: 1.0.8 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index 0c4b718e..fe6d0b73 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -812,7 +812,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Delegators History - description: Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided. + description: Return information about active delegators (incl. history) for a given pool and epoch number (all epochs if not specified). /pool_blocks: #RPC get: tags: diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index 58acc437..b0228133 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -36,12 +36,12 @@ "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2" }, "_asset_policy": { - "m": "d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff", + "m": "750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501", "t": "000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b", "g": "313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e" }, "_asset_name": { - "m": "444f4e545350414d", + "m": "424f4f4b", "t": "54735465737431", "g": "41484c636f696e" }, @@ -68,12 +68,12 @@ "g": 1500 }, "stake_addresses1": { - "m": "stake1uyfmzu5qqy70a8kq4c8rw09q0w0ktfcxppwujejnsh6tyrg5c774g", + "m": "stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250", "t": "stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj", "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2" }, "stake_addresses2": { - "m": "stake1uydhlh7f2kkw9eazct5zyzlrvj32gjnkmt2v5qf6t8rut4qwch8ey", + "m": "stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy", "t": "stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx", "g": "stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd" }, From 2a3075b1580dcc060e79f44627e7698a92640641 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Mon, 24 Oct 2022 20:08:14 +1100 Subject: [PATCH 19/86] Fix bugs reported in specs for koios-1.0.8 (non breaking) (#112) --- specs/results/koiosapi-guild.yaml | 16 +++++++--------- specs/results/koiosapi-mainnet.yaml | 16 +++++++--------- specs/results/koiosapi-testnet.yaml | 16 +++++++--------- specs/templates/2-api-params.yaml | 2 +- specs/templates/4-api-schemas.yaml | 14 ++++++-------- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 030cd923..b3e827ed 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1344,7 +1344,7 @@ components: _asset_name: deprecated: false name: _asset_name - description: Asset Name in hexadecimal format (hex) + description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string example: 41484c636f696e @@ -2785,12 +2785,9 @@ components: description: Withdrawal amount (in lovelaces) example: 9845162 stake_addr: - type: object - properties: - bech32: - type: string - description: A Cardano staking address (reward account, bech32 encoded) - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + type: string + description: A Cardano staking address (reward account, bech32 encoded) + example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj assets_minted: type: array description: Array of minted assets with-in a transaction @@ -3036,7 +3033,6 @@ components: properties: policy_id: type: string - nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: @@ -3065,7 +3061,9 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + allOf: + - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + description: Latest minting transaction metadata (aligns with CIP-25) token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index cbe3506b..1ff3759e 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1344,7 +1344,7 @@ components: _asset_name: deprecated: false name: _asset_name - description: Asset Name in hexadecimal format (hex) + description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string example: 424f4f4b @@ -2785,12 +2785,9 @@ components: description: Withdrawal amount (in lovelaces) example: 9845162 stake_addr: - type: object - properties: - bech32: - type: string - description: A Cardano staking address (reward account, bech32 encoded) - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + type: string + description: A Cardano staking address (reward account, bech32 encoded) + example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj assets_minted: type: array description: Array of minted assets with-in a transaction @@ -3036,7 +3033,6 @@ components: properties: policy_id: type: string - nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: @@ -3065,7 +3061,9 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + allOf: + - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + description: Latest minting transaction metadata (aligns with CIP-25) token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 5dbf0c55..2a8ed995 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1344,7 +1344,7 @@ components: _asset_name: deprecated: false name: _asset_name - description: Asset Name in hexadecimal format (hex) + description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string example: 54735465737431 @@ -2785,12 +2785,9 @@ components: description: Withdrawal amount (in lovelaces) example: 9845162 stake_addr: - type: object - properties: - bech32: - type: string - description: A Cardano staking address (reward account, bech32 encoded) - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + type: string + description: A Cardano staking address (reward account, bech32 encoded) + example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj assets_minted: type: array description: Array of minted assets with-in a transaction @@ -3036,7 +3033,6 @@ components: properties: policy_id: type: string - nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: @@ -3065,7 +3061,9 @@ components: description: Count of total burn transactions example: 5 minting_tx_metadata: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + allOf: + - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + description: Latest minting transaction metadata (aligns with CIP-25) token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry diff --git a/specs/templates/2-api-params.yaml b/specs/templates/2-api-params.yaml index 01f4abfb..f0d43844 100644 --- a/specs/templates/2-api-params.yaml +++ b/specs/templates/2-api-params.yaml @@ -143,7 +143,7 @@ parameters: _asset_name: deprecated: false name: _asset_name - description: Asset Name in hexadecimal format (hex) + description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string example: ##_asset_name_param## diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 1d318afd..069669c7 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -1216,12 +1216,9 @@ schemas: description: Withdrawal amount (in lovelaces) example: 9845162 stake_addr: - type: object - properties: - bech32: - type: string - description: A Cardano staking address (reward account, bech32 encoded) - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + type: string + description: A Cardano staking address (reward account, bech32 encoded) + example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj assets_minted: type: array description: Array of minted assets with-in a transaction @@ -1467,7 +1464,6 @@ schemas: properties: policy_id: type: string - nullable: true description: Asset Policy ID (hex) example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff asset_name: @@ -1496,7 +1492,9 @@ schemas: description: Count of total burn transactions example: 5 minting_tx_metadata: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + allOf: + - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + description: Latest minting transaction metadata (aligns with CIP-25) token_registry_metadata: type: object description: Asset metadata registered on the Cardano Token Registry From ec9f3f4a95e6204cf19a3f84105ca053a5b4b9bd Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Mon, 24 Oct 2022 10:09:03 +0100 Subject: [PATCH 20/86] Update spec for datum_info endpoitn (#117) --- specs/results/koiosapi-guild.yaml | 50 +++++++++++++++++++++++- specs/results/koiosapi-mainnet.yaml | 50 +++++++++++++++++++++++- specs/results/koiosapi-testnet.yaml | 50 +++++++++++++++++++++++- specs/templates/3-api-requestBodies.yaml | 18 ++++++++- specs/templates/4-api-schemas.yaml | 13 +++++- specs/templates/api-main.yaml | 21 ++++++++++ specs/templates/example-map.json | 10 +++++ 7 files changed, 207 insertions(+), 5 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index b3e827ed..c81dbd8f 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1198,6 +1198,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /datum_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/datum_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/datum_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes components: parameters: select: @@ -1566,6 +1587,22 @@ components: - pool19st4a2vvu78tjtyywjte2eml3kx6ynersgd2nyw0y4jvyhlfu0u - pool1uul8pytp2p0xq4ckjn3l294km0m7fuef46teehvh3x5tk46sfx3 - pool1us9ww725p0vygae5zaydme3apt7wg9se2yemhl8mgwkes3tlrqp + datum_hashes: + content: + application/json: + schema: + type: object + properties: + _datum_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano datum hashes + example: + _datum_hashes: + - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 + - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 securitySchemes: {} schemas: tip: @@ -2725,7 +2762,7 @@ components: properties: bytes: type: string - description: Datum (hex) + description: Datum bytes (hex) example: 19029a value: type: object @@ -3237,6 +3274,17 @@ components: type: object description: The actual data in json format example: { "bytes": "3c33" } + datum_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + bytes: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" headers: {} responses: OK: diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 1ff3759e..91f702f5 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1198,6 +1198,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /datum_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/datum_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/datum_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes components: parameters: select: @@ -1566,6 +1587,22 @@ components: - pool100wj94uzf54vup2hdzk0afng4dhjaqggt7j434mtgm8v2gfvfgp - pool102s2nqtea2hf5q0s4amj0evysmfnhrn4apyyhd4azcmsclzm96m - pool102vsulhfx8ua2j9fwl2u7gv57fhhutc3tp6juzaefgrn7ae35wm + datum_hashes: + content: + application/json: + schema: + type: object + properties: + _datum_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano datum hashes + example: + _datum_hashes: + - 818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46 + - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 securitySchemes: {} schemas: tip: @@ -2725,7 +2762,7 @@ components: properties: bytes: type: string - description: Datum (hex) + description: Datum bytes (hex) example: 19029a value: type: object @@ -3237,6 +3274,17 @@ components: type: object description: The actual data in json format example: { "bytes": "3c33" } + datum_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + bytes: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" headers: {} responses: OK: diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 2a8ed995..dd31dcb4 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1198,6 +1198,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /datum_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/datum_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/datum_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes components: parameters: select: @@ -1566,6 +1587,22 @@ components: - pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh - pool102x86jz7uus6p6mlw02fdw2s805kng7g6ujs6s342t5msk36tch - pool103qt58f9xlsr7y9anz3lnyq6cph4xh2yr4qrrtc356ldzz6ktqz + datum_hashes: + content: + application/json: + schema: + type: object + properties: + _datum_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano datum hashes + example: + _datum_hashes: + - 5865d96e4313780a37af26055cdcc90308db23adaf4f2082178355e5e1dc20f6 + - 4932dce28712ccc4858e3d83cc8e79b12740f66007dc9a287bb640a264c899de securitySchemes: {} schemas: tip: @@ -2725,7 +2762,7 @@ components: properties: bytes: type: string - description: Datum (hex) + description: Datum bytes (hex) example: 19029a value: type: object @@ -3237,6 +3274,17 @@ components: type: object description: The actual data in json format example: { "bytes": "3c33" } + datum_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + bytes: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" headers: {} responses: OK: diff --git a/specs/templates/3-api-requestBodies.yaml b/specs/templates/3-api-requestBodies.yaml index 50894d07..4bae7a8f 100644 --- a/specs/templates/3-api-requestBodies.yaml +++ b/specs/templates/3-api-requestBodies.yaml @@ -182,4 +182,20 @@ requestBodies: _pool_bech32_ids: - ##pool_ids_pool_bech32_ids1_rb## - ##pool_ids_pool_bech32_ids2_rb## - - ##pool_ids_pool_bech32_ids3_rb## \ No newline at end of file + - ##pool_ids_pool_bech32_ids3_rb## + datum_hashes: + content: + application/json: + schema: + type: object + properties: + _datum_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano datum hashes + example: + _datum_hashes: + - ##datum_hashes1_rb## + - ##datum_hashes2_rb## \ No newline at end of file diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 069669c7..b551be04 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -1156,7 +1156,7 @@ schemas: properties: bytes: type: string - description: Datum (hex) + description: Datum bytes (hex) example: 19029a value: type: object @@ -1668,3 +1668,14 @@ schemas: type: object description: The actual data in json format example: { "bytes": "3c33" } + datum_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + bytes: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" \ No newline at end of file diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index fe6d0b73..c5448a4f 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -982,6 +982,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /datum_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/datum_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/datum_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes components: #!params!# #!requestBodies!# diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index b0228133..4f92379d 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -146,6 +146,16 @@ "m": "pool102vsulhfx8ua2j9fwl2u7gv57fhhutc3tp6juzaefgrn7ae35wm", "t": "pool103qt58f9xlsr7y9anz3lnyq6cph4xh2yr4qrrtc356ldzz6ktqz", "g": "pool1us9ww725p0vygae5zaydme3apt7wg9se2yemhl8mgwkes3tlrqp" + }, + "datum_hashes1": { + "m": "818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46", + "t": "5865d96e4313780a37af26055cdcc90308db23adaf4f2082178355e5e1dc20f6", + "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0" + }, + "datum_hashes2": { + "m": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", + "t": "4932dce28712ccc4858e3d83cc8e79b12740f66007dc9a287bb640a264c899de", + "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0" } } } From 4cccfe4e2ec4fbf36d294dc7bf7921e9b288e3c2 Mon Sep 17 00:00:00 2001 From: Dostrelith Date: Mon, 31 Oct 2022 06:07:13 +0000 Subject: [PATCH 21/86] [SPEC] Update schema for asset object consistency (#120) ## Description To go along with https://github.com/cardano-community/guild-operators/pull/1559 Co-authored-by: rdlrt <3169068+rdlrt@users.noreply.github.com> --- specs/results/koiosapi-guild.yaml | 60 ++++++++++++---------------- specs/results/koiosapi-mainnet.yaml | 60 ++++++++++++---------------- specs/results/koiosapi-testnet.yaml | 60 ++++++++++++---------------- specs/templates/4-api-schemas.yaml | 62 ++++++++++++----------------- 4 files changed, 101 insertions(+), 141 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index c81dbd8f..48eaf8d6 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2462,7 +2462,7 @@ components: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + $ref: "#/components/schemas/account_assets/items/properties/asset_list" address_txs: type: array items: @@ -2483,8 +2483,8 @@ components: properties: address: $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" - assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -2607,26 +2607,21 @@ components: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - assets: + asset_list: type: array items: type: object properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - assets: - type: array - items: - type: object - properties: - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - balance: - type: string - description: Asset quantity owned by account - example: 990000 + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -2804,12 +2799,12 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string description: Quantity of assets on the UTxO example: 1 - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2835,9 +2830,11 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string - description: Sum of minted assets (negative on burn) + description: Quantity of minted assets (negative on burn) example: 1 metadata: $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -3016,21 +3013,10 @@ components: properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_names: - type: object - properties: - hex: - type: array - description: Asset Name (hex) - items: - type: string - example: "\x444f4e545350414d" - ascii: - type: array - description: Asset Name (ASCII) - items: - type: string - example: "DONTSPAM" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) @@ -3052,6 +3038,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" total_transactions: type: integer description: Total number of transactions including the given asset @@ -3140,6 +3128,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_txs: type: array description: Array of all mint/burn transactions for an asset diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 91f702f5..b8f9e182 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2462,7 +2462,7 @@ components: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + $ref: "#/components/schemas/account_assets/items/properties/asset_list" address_txs: type: array items: @@ -2483,8 +2483,8 @@ components: properties: address: $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" - assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -2607,26 +2607,21 @@ components: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - assets: + asset_list: type: array items: type: object properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - assets: - type: array - items: - type: object - properties: - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - balance: - type: string - description: Asset quantity owned by account - example: 990000 + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -2804,12 +2799,12 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string description: Quantity of assets on the UTxO example: 1 - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2835,9 +2830,11 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string - description: Sum of minted assets (negative on burn) + description: Quantity of minted assets (negative on burn) example: 1 metadata: $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -3016,21 +3013,10 @@ components: properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_names: - type: object - properties: - hex: - type: array - description: Asset Name (hex) - items: - type: string - example: "\x444f4e545350414d" - ascii: - type: array - description: Asset Name (ASCII) - items: - type: string - example: "DONTSPAM" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) @@ -3052,6 +3038,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" total_transactions: type: integer description: Total number of transactions including the given asset @@ -3140,6 +3128,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_txs: type: array description: Array of all mint/burn transactions for an asset diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index dd31dcb4..ad59c3c9 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2462,7 +2462,7 @@ components: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + $ref: "#/components/schemas/account_assets/items/properties/asset_list" address_txs: type: array items: @@ -2483,8 +2483,8 @@ components: properties: address: $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" - assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -2607,26 +2607,21 @@ components: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - assets: + asset_list: type: array items: type: object properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - assets: - type: array - items: - type: object - properties: - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - balance: - type: string - description: Asset quantity owned by account - example: 990000 + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -2804,12 +2799,12 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string description: Quantity of assets on the UTxO example: 1 - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -2835,9 +2830,11 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string - description: Sum of minted assets (negative on burn) + description: Quantity of minted assets (negative on burn) example: 1 metadata: $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -3016,21 +3013,10 @@ components: properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_names: - type: object - properties: - hex: - type: array - description: Asset Name (hex) - items: - type: string - example: "\x444f4e545350414d" - ascii: - type: array - description: Asset Name (ASCII) - items: - type: string - example: "DONTSPAM" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) @@ -3052,6 +3038,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" total_transactions: type: integer description: Total number of transactions including the given asset @@ -3140,6 +3128,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_txs: type: array description: Array of all mint/burn transactions for an asset diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index b551be04..164e2a46 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -856,7 +856,7 @@ schemas: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + $ref: "#/components/schemas/account_assets/items/properties/asset_list" address_txs: type: array items: @@ -877,8 +877,8 @@ schemas: properties: address: $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" - assets: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/asset_list" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: $ref: "#/components/schemas/address_txs" account_list: @@ -1001,26 +1001,21 @@ schemas: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - assets: + asset_list: type: array items: type: object properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - assets: - type: array - items: - type: object - properties: - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - asset_policy: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - balance: - type: string - description: Asset quantity owned by account - example: 990000 + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Asset quantity owned by account + example: 990000 account_history: type: array items: @@ -1198,12 +1193,12 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string description: Quantity of assets on the UTxO example: 1 - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -1229,9 +1224,11 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" quantity: type: string - description: Sum of minted assets (negative on burn) + description: Quantity of minted assets (negative on burn) example: 1 metadata: $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -1410,21 +1407,10 @@ schemas: properties: policy_id: $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_names: - type: object - properties: - hex: - type: array - description: Asset Name (hex) - items: - type: string - example: "\x444f4e545350414d" - ascii: - type: array - description: Asset Name (ASCII) - items: - type: string - example: "DONTSPAM" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) @@ -1446,6 +1432,8 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" total_transactions: type: integer description: Total number of transactions including the given asset @@ -1534,6 +1522,8 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/policy_id" asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" minting_txs: type: array description: Array of all mint/burn transactions for an asset @@ -1678,4 +1668,4 @@ schemas: value: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" bytes: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" \ No newline at end of file + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" From 2423de37e437f990861e5a90639df3ee2d3988c9 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Tue, 8 Nov 2022 13:32:45 +1100 Subject: [PATCH 22/86] Koios v1.0.9rc (#122) ## Description This is a release candidate branch - to use and test against guildnet repo, not release itself. Thus, changelog is not yet updated - [x] Bump version to v1.0.9rc - [x] Add files/grest from guild-operators repo (as part of #118 ) - [x] Update schemathesis run command (add header CT: json to request, and remove hypothesis seed references) - [x] Fix asset_history endpoint (metadata => wrap in array to refer to right object) - [x] Add `_history` parameter to `asset_txs` (and default to `false`) that allows querying only current UTxO set - [x] Add examples to specs for preview & preprod - [x] `pool_history` - `fixed_cost`, `pool_fees`, `deleg_rewards`, `epoch_ros` should return as 0 when null - [x] Fix specs/examples based on schemathesis test outputs ## How has this been tested? Updated on guildnet, preprod, preview Co-authored-by: Ola Co-authored-by: KoT_B_KocMoce <49576827+hodlonaut@users.noreply.github.com> Co-authored-by: Dostrelith --- .../cron/jobs/active-stake-cache-update.sh | 37 + .../grest/cron/jobs/asset-registry-update.sh | 54 + .../cron/jobs/epoch-info-cache-update.sh | 6 + .../cron/jobs/pool-history-cache-update.sh | 6 + .../cron/jobs/populate-next-epoch-nonce.sh | 20 + .../stake-distribution-new-accounts-update.sh | 6 + .../cron/jobs/stake-distribution-update.sh | 6 + files/grest/cron/jobs/stake-snapshot-cache.sh | 6 + files/grest/rpc/00_blockchain/genesis.sql | 36 + files/grest/rpc/00_blockchain/tip.sql | 30 + files/grest/rpc/00_blockchain/totals.sql | 36 + .../01_cached_tables/active_stake_cache.sql | 193 + .../01_cached_tables/asset_registry_cache.sql | 59 + .../rpc/01_cached_tables/epoch_info_cache.sql | 290 ++ .../01_cached_tables/pool_history_cache.sql | 260 ++ .../rpc/01_cached_tables/pool_info_cache.sql | 340 ++ .../stake_distribution_cache.sql | 249 ++ .../stake_distribution_new_accounts.sql | 92 + .../01_cached_tables/stake_snapshot_cache.sql | 400 ++ files/grest/rpc/account/account_addresses.sql | 40 + files/grest/rpc/account/account_assets.sql | 63 + files/grest/rpc/account/account_history.sql | 63 + files/grest/rpc/account/account_info.sql | 186 + .../grest/rpc/account/account_info_cached.sql | 113 + files/grest/rpc/account/account_rewards.sql | 66 + files/grest/rpc/account/account_updates.sql | 79 + files/grest/rpc/address/address_assets.sql | 54 + files/grest/rpc/address/address_info.sql | 121 + files/grest/rpc/address/address_txs.sql | 54 + files/grest/rpc/address/credential_txs.sql | 64 + files/grest/rpc/assets/asset_address_list.sql | 42 + files/grest/rpc/assets/asset_history.sql | 77 + files/grest/rpc/assets/asset_info.sql | 115 + files/grest/rpc/assets/asset_policy_info.sql | 114 + files/grest/rpc/assets/asset_summary.sql | 81 + files/grest/rpc/assets/asset_txs.sql | 64 + files/grest/rpc/blocks/block_info.sql | 116 + files/grest/rpc/blocks/block_txs.sql | 43 + .../grest/rpc/epoch/epoch_block_protocols.sql | 38 + files/grest/rpc/epoch/epoch_info.sql | 54 + files/grest/rpc/epoch/epoch_params.sql | 121 + files/grest/rpc/pool/pool_blocks.sql | 36 + files/grest/rpc/pool/pool_delegators.sql | 47 + .../rpc/pool/pool_delegators_history.sql | 47 + files/grest/rpc/pool/pool_history.sql | 35 + files/grest/rpc/pool/pool_info.sql | 171 + files/grest/rpc/pool/pool_list.sql | 37 + files/grest/rpc/pool/pool_metadata.sql | 34 + files/grest/rpc/pool/pool_relays.sql | 23 + files/grest/rpc/pool/pool_stake_snapshot.sql | 45 + files/grest/rpc/pool/pool_updates.sql | 56 + files/grest/rpc/script/datum_info.sql | 28 + files/grest/rpc/script/native_script_list.sql | 23 + files/grest/rpc/script/plutus_script_list.sql | 19 + files/grest/rpc/script/script_redeemers.sql | 42 + files/grest/rpc/transactions/tx_info.sql | 828 ++++ files/grest/rpc/transactions/tx_metadata.sql | 41 + .../grest/rpc/transactions/tx_metalabels.sql | 21 + files/grest/rpc/transactions/tx_status.sql | 26 + files/grest/rpc/transactions/tx_utxos.sql | 156 + files/grest/rpc/views/account_list.sql | 10 + files/grest/rpc/views/asset_list.sql | 12 + files/grest/rpc/views/blocks.sql | 27 + specs/createspecs.py | 2 + specs/results/koiosapi-guild.yaml | 49 +- specs/results/koiosapi-mainnet.yaml | 57 +- specs/results/koiosapi-preprod.yaml | 3353 +++++++++++++++++ specs/results/koiosapi-preview.yaml | 3353 +++++++++++++++++ specs/results/koiosapi-testnet.yaml | 49 +- specs/templates/1-api-info.yaml | 2 +- specs/templates/2-api-params.yaml | 14 +- specs/templates/4-api-schemas.yaml | 7 +- specs/templates/api-main.yaml | 26 +- specs/templates/example-map.json | 132 +- tests/setup-tests.sh | 4 +- topology/topology-preprod.json | 8 + topology/topology-preview.json | 8 + 77 files changed, 12653 insertions(+), 69 deletions(-) create mode 100644 files/grest/cron/jobs/active-stake-cache-update.sh create mode 100644 files/grest/cron/jobs/asset-registry-update.sh create mode 100644 files/grest/cron/jobs/epoch-info-cache-update.sh create mode 100644 files/grest/cron/jobs/pool-history-cache-update.sh create mode 100644 files/grest/cron/jobs/populate-next-epoch-nonce.sh create mode 100644 files/grest/cron/jobs/stake-distribution-new-accounts-update.sh create mode 100644 files/grest/cron/jobs/stake-distribution-update.sh create mode 100644 files/grest/cron/jobs/stake-snapshot-cache.sh create mode 100644 files/grest/rpc/00_blockchain/genesis.sql create mode 100644 files/grest/rpc/00_blockchain/tip.sql create mode 100644 files/grest/rpc/00_blockchain/totals.sql create mode 100644 files/grest/rpc/01_cached_tables/active_stake_cache.sql create mode 100644 files/grest/rpc/01_cached_tables/asset_registry_cache.sql create mode 100644 files/grest/rpc/01_cached_tables/epoch_info_cache.sql create mode 100644 files/grest/rpc/01_cached_tables/pool_history_cache.sql create mode 100644 files/grest/rpc/01_cached_tables/pool_info_cache.sql create mode 100644 files/grest/rpc/01_cached_tables/stake_distribution_cache.sql create mode 100644 files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql create mode 100644 files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql create mode 100644 files/grest/rpc/account/account_addresses.sql create mode 100644 files/grest/rpc/account/account_assets.sql create mode 100644 files/grest/rpc/account/account_history.sql create mode 100644 files/grest/rpc/account/account_info.sql create mode 100644 files/grest/rpc/account/account_info_cached.sql create mode 100644 files/grest/rpc/account/account_rewards.sql create mode 100644 files/grest/rpc/account/account_updates.sql create mode 100644 files/grest/rpc/address/address_assets.sql create mode 100644 files/grest/rpc/address/address_info.sql create mode 100644 files/grest/rpc/address/address_txs.sql create mode 100644 files/grest/rpc/address/credential_txs.sql create mode 100644 files/grest/rpc/assets/asset_address_list.sql create mode 100644 files/grest/rpc/assets/asset_history.sql create mode 100644 files/grest/rpc/assets/asset_info.sql create mode 100644 files/grest/rpc/assets/asset_policy_info.sql create mode 100644 files/grest/rpc/assets/asset_summary.sql create mode 100644 files/grest/rpc/assets/asset_txs.sql create mode 100644 files/grest/rpc/blocks/block_info.sql create mode 100644 files/grest/rpc/blocks/block_txs.sql create mode 100644 files/grest/rpc/epoch/epoch_block_protocols.sql create mode 100644 files/grest/rpc/epoch/epoch_info.sql create mode 100644 files/grest/rpc/epoch/epoch_params.sql create mode 100644 files/grest/rpc/pool/pool_blocks.sql create mode 100644 files/grest/rpc/pool/pool_delegators.sql create mode 100644 files/grest/rpc/pool/pool_delegators_history.sql create mode 100644 files/grest/rpc/pool/pool_history.sql create mode 100644 files/grest/rpc/pool/pool_info.sql create mode 100644 files/grest/rpc/pool/pool_list.sql create mode 100644 files/grest/rpc/pool/pool_metadata.sql create mode 100644 files/grest/rpc/pool/pool_relays.sql create mode 100644 files/grest/rpc/pool/pool_stake_snapshot.sql create mode 100644 files/grest/rpc/pool/pool_updates.sql create mode 100644 files/grest/rpc/script/datum_info.sql create mode 100644 files/grest/rpc/script/native_script_list.sql create mode 100644 files/grest/rpc/script/plutus_script_list.sql create mode 100644 files/grest/rpc/script/script_redeemers.sql create mode 100644 files/grest/rpc/transactions/tx_info.sql create mode 100644 files/grest/rpc/transactions/tx_metadata.sql create mode 100644 files/grest/rpc/transactions/tx_metalabels.sql create mode 100644 files/grest/rpc/transactions/tx_status.sql create mode 100644 files/grest/rpc/transactions/tx_utxos.sql create mode 100644 files/grest/rpc/views/account_list.sql create mode 100644 files/grest/rpc/views/asset_list.sql create mode 100644 files/grest/rpc/views/blocks.sql create mode 100644 specs/results/koiosapi-preprod.yaml create mode 100644 specs/results/koiosapi-preview.yaml create mode 100644 topology/topology-preprod.json create mode 100644 topology/topology-preview.json diff --git a/files/grest/cron/jobs/active-stake-cache-update.sh b/files/grest/cron/jobs/active-stake-cache-update.sh new file mode 100644 index 00000000..2cab81b6 --- /dev/null +++ b/files/grest/cron/jobs/active-stake-cache-update.sh @@ -0,0 +1,37 @@ +#!/bin/bash +DB_NAME=cexplorer + +echo "$(date +%F_%H:%M:%S) Running active stake cache update..." + +# High level check in db to see if update needed at all (should be updated only once on epoch transition) +[[ $(psql ${DB_NAME} -qbt -c "SELECT grest.active_stake_cache_update_check();" | tail -2 | tr -cd '[:alnum:]') != 't' ]] && + echo "No update needed, exiting..." && + exit 0 + +# This could break due to upstream changes on db-sync (based on log format) +last_epoch_stakes_log=$(grep -r 'Inserted.*.EpochStake for EpochNo ' "$(dirname "$0")"/../../logs/dbsync-*.json "$(dirname "$0")"/../../logs/archive/dbsync-*.json 2>/dev/null | sed -e 's#.*.Inserted ##' -e 's#EpochStake for EpochNo##' -e 's#\"}.*.$##' | sort -k2 -n | tail -1) +[[ -z ${last_epoch_stakes_log} ]] && + echo "Could not find any 'Handling stakes' log entries, exiting..." && + exit 1 + +logs_last_epoch_stakes_count=$(echo "${last_epoch_stakes_log}" | cut -d\ -f1) +logs_last_epoch_no=$(echo "${last_epoch_stakes_log}" | cut -d\ -f3) + +db_last_epoch_no=$(psql ${DB_NAME} -qbt -c "SELECT MAX(NO) from EPOCH;" | tr -cd '[:alnum:]') +[[ "${db_last_epoch_no}" != "${logs_last_epoch_no}" ]] && + echo "Mismatch between last epoch in logs and database, exiting..." && + exit 1 + +# Count current epoch entries processed by db-sync +db_epoch_stakes_count=$(psql ${DB_NAME} -qbt -c "SELECT COUNT(1) FROM EPOCH_STAKE WHERE epoch_no = ${db_last_epoch_no};" | tr -cd '[:alnum:]') + +# Check if db-sync completed handling stakes +[[ "${db_epoch_stakes_count}" != "${logs_last_epoch_stakes_count}" ]] && + echo "Logs last epoch stakes count: ${logs_last_epoch_stakes_count}" && + echo "DB last epoch stakes count: ${db_epoch_stakes_count}" && + echo "db-sync stakes handling still incomplete, exiting..." && + exit 0 + +# Stakes have been validated, run the cache update +psql ${DB_NAME} -qbt -c "SELECT GREST.active_stake_cache_update(${db_last_epoch_no});" 2>&1 1>/dev/null +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/cron/jobs/asset-registry-update.sh b/files/grest/cron/jobs/asset-registry-update.sh new file mode 100644 index 00000000..645ec404 --- /dev/null +++ b/files/grest/cron/jobs/asset-registry-update.sh @@ -0,0 +1,54 @@ +#!/bin/bash +CNODE_VNAME=cnode +DB_NAME=cexplorer +TR_URL=https://github.com/cardano-foundation/cardano-token-registry +TR_SUBDIR=mappings +TR_DIR=${HOME}/git +TR_NAME=${CNODE_VNAME}-token-registry + +echo "$(date +%F_%H:%M:%S) - START - Asset Registry Update" + +if [[ ! -d "${TR_DIR}/${TR_NAME}" ]]; then + [[ -z ${HOME} ]] && echo "HOME variable not set, aborting..." && exit 1 + mkdir -p "${TR_DIR}" + cd "${TR_DIR}" >/dev/null || exit 1 + git clone ${TR_URL} ${TR_NAME} >/dev/null || exit 1 +fi +pushd "${TR_DIR}/${TR_NAME}" >/dev/null || exit 1 +git pull >/dev/null || exit 1 + +last_commit="$(psql ${DB_NAME} -c "select last_value from grest.control_table where key='asset_registry_commit'" -t | xargs)" +[[ -z "${last_commit}" ]] && last_commit="$(git rev-list HEAD | tail -n 1)" +latest_commit="$(git rev-list HEAD | head -n 1)" + +[[ "${last_commit}" == "${latest_commit}" ]] && echo "$(date +%F_%H:%M:%S) - END - Asset Registry Update, no updates necessary." && exit 0 + +asset_cnt=0 + +[[ -f '.assetregistry.csv' ]] && rm -f .assetregistry.csv +while IFS= read -re assetfile; do + if ! asset_data_csv=$(jq -er '[ + .subject[0:56], + .subject[56:], + .name.value, + .description.value // "", + .ticker.value // "", + .url.value // "", + .logo.value // "", + .decimals.value // 0 + ] | @csv' "${assetfile}"); then + echo "Failure parsing '${assetfile}', skipping..." + continue + fi + echo "${asset_data_csv}" >> .assetregistry.csv + ((asset_cnt++)) +done < <(git diff --name-only "${last_commit}" "${latest_commit}" | grep ^${TR_SUBDIR}) +cat << EOF > .assetregistry.sql +CREATE TEMP TABLE tmparc (like grest.asset_registry_cache); +\COPY tmparc FROM '.assetregistry.csv' DELIMITER ',' CSV; +INSERT INTO grest.asset_registry_cache SELECT DISTINCT ON (asset_policy,asset_name) * FROM tmparc ON CONFLICT(asset_policy,asset_name) DO UPDATE SET asset_policy=excluded.asset_policy, asset_name=excluded.asset_name, name=excluded.name, description=excluded.description, ticker=excluded.ticker, url=excluded.url, logo=excluded.logo,decimals=excluded.decimals; +EOF + +psql ${DB_NAME} -qb -f .assetregistry.sql >/dev/null && rm -f .assetregistry.sql +psql ${DB_NAME} -qb -c "INSERT INTO grest.control_table (key, last_value) VALUES ('asset_registry_commit','${latest_commit}') ON CONFLICT(key) DO UPDATE SET last_value='${latest_commit}'" +echo "$(date +%F_%H:%M:%S) - END - Asset Registry Update, ${asset_cnt} assets added/updated for commits ${last_commit} to ${latest_commit}." diff --git a/files/grest/cron/jobs/epoch-info-cache-update.sh b/files/grest/cron/jobs/epoch-info-cache-update.sh new file mode 100644 index 00000000..e57a7695 --- /dev/null +++ b/files/grest/cron/jobs/epoch-info-cache-update.sh @@ -0,0 +1,6 @@ +#!/bin/bash +DB_NAME=cexplorer + +echo "$(date +%F_%H:%M:%S) Running epoch info cache update..." +psql ${DB_NAME} -qbt -c "SELECT GREST.EPOCH_INFO_CACHE_UPDATE();" 2>&1 1>/dev/null +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/cron/jobs/pool-history-cache-update.sh b/files/grest/cron/jobs/pool-history-cache-update.sh new file mode 100644 index 00000000..8d29d518 --- /dev/null +++ b/files/grest/cron/jobs/pool-history-cache-update.sh @@ -0,0 +1,6 @@ +#!/bin/bash +DB_NAME=cexplorer + +echo "$(date +%F_%H:%M:%S) Running pool history cache update..." +psql ${DB_NAME} -qbt -c "SELECT GREST.pool_history_cache_update();" 2>&1 1>/dev/null +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/cron/jobs/populate-next-epoch-nonce.sh b/files/grest/cron/jobs/populate-next-epoch-nonce.sh new file mode 100644 index 00000000..5ccee478 --- /dev/null +++ b/files/grest/cron/jobs/populate-next-epoch-nonce.sh @@ -0,0 +1,20 @@ +#!/bin/bash +DB_NAME=cexplorer +NWMAGIC= +EPOCH_LENGTH= +PROM_URL= +CCLI= +export CARDANO_NODE_SOCKET_PATH= + +echo "$(date +%F_%H:%M:%S) Running next epoch nonce calculation..." + +min_slot=$((EPOCH_LENGTH * 7 / 10)) +current_epoch=$(curl -s "${PROM_URL}" | grep epoch | awk '{print $2}') +current_slot_in_epoch=$(curl -s "${PROM_URL}" | grep slotInEpoch | awk '{print $2}') +next_epoch=$((current_epoch + 1)) + +[[ ${current_slot_in_epoch} -ge ${min_slot} ]] && + next_epoch_nonce=$(echo "$(${CCLI} query protocol-state --testnet-magic "${NWMAGIC}" | jq -r .candidateNonce.contents)$(${CCLI} query protocol-state --testnet-magic "${NWMAGIC}" | jq -r .lastEpochBlockNonce.contents)" | xxd -r -p | b2sum -b -l 256 | awk '{print $1}') && + psql ${DB_NAME} -c "INSERT INTO grest.epoch_info_cache (epoch_no, p_nonce) VALUES (${next_epoch}, '${next_epoch_nonce}') ON CONFLICT DO NOTHING;" + +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/cron/jobs/stake-distribution-new-accounts-update.sh b/files/grest/cron/jobs/stake-distribution-new-accounts-update.sh new file mode 100644 index 00000000..6978180a --- /dev/null +++ b/files/grest/cron/jobs/stake-distribution-new-accounts-update.sh @@ -0,0 +1,6 @@ +#!/bin/bash +DB_NAME=cexplorer + +echo "$(date +%F_%H:%M:%S) Running stake distribution update for new accounts..." +psql ${DB_NAME} -qbt -c "CALL GREST.UPDATE_NEWLY_REGISTERED_ACCOUNTS_STAKE_DISTRIBUTION_CACHE();" 2>&1 1>/dev/null +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/cron/jobs/stake-distribution-update.sh b/files/grest/cron/jobs/stake-distribution-update.sh new file mode 100644 index 00000000..be51c212 --- /dev/null +++ b/files/grest/cron/jobs/stake-distribution-update.sh @@ -0,0 +1,6 @@ +#!/bin/bash +DB_NAME=cexplorer + +echo "$(date +%F_%H:%M:%S) Running stake distribution update..." +psql ${DB_NAME} -qbt -c "SELECT GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK();" 2>&1 1>/dev/null +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/cron/jobs/stake-snapshot-cache.sh b/files/grest/cron/jobs/stake-snapshot-cache.sh new file mode 100644 index 00000000..3b0f9b02 --- /dev/null +++ b/files/grest/cron/jobs/stake-snapshot-cache.sh @@ -0,0 +1,6 @@ +#!/bin/bash +DB_NAME=cexplorer + +echo "$(date +%F_%H:%M:%S) Capturing last epochs' snapshot..." +psql ${DB_NAME} -qbt -c "CALL GREST.CAPTURE_LAST_EPOCH_SNAPSHOT();" 2>&1 1>/dev/null +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/rpc/00_blockchain/genesis.sql b/files/grest/rpc/00_blockchain/genesis.sql new file mode 100644 index 00000000..9746e282 --- /dev/null +++ b/files/grest/rpc/00_blockchain/genesis.sql @@ -0,0 +1,36 @@ +CREATE FUNCTION grest.genesis () + RETURNS TABLE ( + NETWORKMAGIC varchar, + NETWORKID varchar, + ACTIVESLOTCOEFF varchar, + UPDATEQUORUM varchar, + MAXLOVELACESUPPLY varchar, + EPOCHLENGTH varchar, + SYSTEMSTART integer, + SLOTSPERKESPERIOD varchar, + SLOTLENGTH varchar, + MAXKESREVOLUTIONS varchar, + SECURITYPARAM varchar, + ALONZOGENESIS varchar + ) + LANGUAGE PLPGSQL + AS $$ +BEGIN + RETURN QUERY + SELECT + g.NETWORKMAGIC, + g.NETWORKID, + g.ACTIVESLOTCOEFF, + g.UPDATEQUORUM, + g.MAXLOVELACESUPPLY, + g.EPOCHLENGTH, + EXTRACT(epoch from g.SYSTEMSTART::timestamp)::integer, + g.SLOTSPERKESPERIOD, + g.SLOTLENGTH, + g.MAXKESREVOLUTIONS, + g.SECURITYPARAM, + g.ALONZOGENESIS + FROM + grest.genesis g; +END; +$$; diff --git a/files/grest/rpc/00_blockchain/tip.sql b/files/grest/rpc/00_blockchain/tip.sql new file mode 100644 index 00000000..eb4539c5 --- /dev/null +++ b/files/grest/rpc/00_blockchain/tip.sql @@ -0,0 +1,30 @@ +CREATE FUNCTION grest.tip () + RETURNS TABLE ( + hash text, + epoch_no word31type, + abs_slot word63type, + epoch_slot word31type, + block_no word31type, + block_time integer + ) + LANGUAGE PLPGSQL + AS $$ +BEGIN + RETURN QUERY + SELECT + ENCODE(B.HASH::bytea, 'hex') AS BLOCK_HASH, + b.EPOCH_NO AS EPOCH_NO, + b.SLOT_NO AS ABS_SLOT, + b.EPOCH_SLOT_NO AS EPOCH_SLOT, + b.BLOCK_NO, + EXTRACT(EPOCH from b.TIME)::integer + FROM + BLOCK B + ORDER BY + B.ID DESC + LIMIT 1; +END; +$$; + +COMMENT ON FUNCTION grest.tip IS 'Get the tip info about the latest block seen by chain'; + diff --git a/files/grest/rpc/00_blockchain/totals.sql b/files/grest/rpc/00_blockchain/totals.sql new file mode 100644 index 00000000..bb562f08 --- /dev/null +++ b/files/grest/rpc/00_blockchain/totals.sql @@ -0,0 +1,36 @@ +CREATE FUNCTION grest.totals (_epoch_no numeric DEFAULT NULL) + RETURNS TABLE ( + epoch_no word31type, + circulation text, + treasury text, + reward text, + supply text, + reserves text + ) + LANGUAGE PLPGSQL + AS $$ +BEGIN + IF _epoch_no IS NULL THEN + RETURN QUERY ( + SELECT + ap.epoch_no, ap.utxo::text, ap.treasury::text, ap.rewards::text, (ap.treasury + ap.rewards + ap.utxo + ap.deposits + ap.fees)::text as supply, ap.reserves::text + FROM + public.ada_pots as ap + ORDER BY + ap.epoch_no DESC) ; + ELSE + RETURN QUERY ( + SELECT + ap.epoch_no, ap.utxo::text, ap.treasury::text, ap.rewards::text, (ap.treasury + ap.rewards + ap.utxo + ap.deposits + ap.fees)::text as supply, ap.reserves::text + FROM + public.ada_pots as ap + WHERE + ap.epoch_no = _epoch_no + ORDER BY + ap.epoch_no DESC); + END IF; +END; +$$; + +COMMENT ON FUNCTION grest.totals IS 'Get the circulating utxo, treasury, rewards, supply and reserves in lovelace for specified epoch, all epochs if empty'; + diff --git a/files/grest/rpc/01_cached_tables/active_stake_cache.sql b/files/grest/rpc/01_cached_tables/active_stake_cache.sql new file mode 100644 index 00000000..092f036c --- /dev/null +++ b/files/grest/rpc/01_cached_tables/active_stake_cache.sql @@ -0,0 +1,193 @@ +CREATE TABLE IF NOT EXISTS GREST.POOL_ACTIVE_STAKE_CACHE ( + POOL_ID varchar NOT NULL, + EPOCH_NO bigint NOT NULL, + AMOUNT LOVELACE NOT NULL, + PRIMARY KEY (POOL_ID, EPOCH_NO) +); + +CREATE TABLE IF NOT EXISTS GREST.EPOCH_ACTIVE_STAKE_CACHE ( + EPOCH_NO bigint NOT NULL, + AMOUNT LOVELACE NOT NULL, + PRIMARY KEY (EPOCH_NO) +); + +CREATE TABLE IF NOT EXISTS GREST.ACCOUNT_ACTIVE_STAKE_CACHE ( + STAKE_ADDRESS varchar NOT NULL, + POOL_ID varchar NOT NULL, + EPOCH_NO bigint NOT NULL, + AMOUNT LOVELACE NOT NULL, + PRIMARY KEY (STAKE_ADDRESS, POOL_ID, EPOCH_NO) +); + +CREATE FUNCTION grest.active_stake_cache_update_check () + RETURNS BOOLEAN + LANGUAGE plpgsql + AS +$$ + DECLARE + _current_epoch_no integer; + _last_active_stake_validated_epoch text; + BEGIN + + -- Get Last Active Stake Validated Epoch + SELECT last_value + INTO _last_active_stake_validated_epoch + FROM + grest.control_table + WHERE + key = 'last_active_stake_validated_epoch'; + + -- Get Current Epoch + SELECT MAX(NO) + INTO _current_epoch_no + FROM epoch; + + RAISE NOTICE 'Current epoch: %', + _current_epoch_no; + RAISE NOTICE 'Last active stake validated epoch: %', + _last_active_stake_validated_epoch; + + IF + _current_epoch_no > COALESCE(_last_active_stake_validated_epoch::integer, 0) + THEN + RETURN TRUE; + END IF; + + RETURN FALSE; + END; +$$; + +COMMENT ON FUNCTION grest.active_stake_cache_update_check + IS 'Internal function to determine whether active stake cache should be updated'; + +CREATE FUNCTION grest.active_stake_cache_update (_epoch_no integer) + RETURNS VOID + LANGUAGE plpgsql + AS +$$ + DECLARE + _last_pool_active_stake_cache_epoch_no integer; + _last_epoch_active_stake_cache_epoch_no integer; + _last_account_active_stake_cache_epoch_no integer; + BEGIN + + /* CHECK PREVIOUS QUERY FINISHED RUNNING */ + IF ( + SELECT + COUNT(pid) > 1 + FROM + pg_stat_activity + WHERE + state = 'active' + AND + query ILIKE '%grest.active_stake_cache_update(%' + AND + datname = ( + SELECT + current_database() + ) + ) THEN + RAISE EXCEPTION + 'Previous query still running but should have completed! Exiting...'; + END IF; + + /* POOL ACTIVE STAKE CACHE */ + SELECT + COALESCE(MAX(epoch_no), 0) + INTO + _last_pool_active_stake_cache_epoch_no + FROM + GREST.POOL_ACTIVE_STAKE_CACHE; + + INSERT INTO GREST.POOL_ACTIVE_STAKE_CACHE + SELECT + POOL_HASH.VIEW AS POOL_ID, + EPOCH_STAKE.EPOCH_NO, + SUM(EPOCH_STAKE.AMOUNT) AS AMOUNT + FROM + PUBLIC.EPOCH_STAKE + INNER JOIN PUBLIC.POOL_HASH ON POOL_HASH.ID = EPOCH_STAKE.POOL_ID + WHERE + EPOCH_STAKE.EPOCH_NO >= _last_pool_active_stake_cache_epoch_no + AND + EPOCH_STAKE.EPOCH_NO <= _epoch_no + GROUP BY + POOL_HASH.VIEW, + EPOCH_STAKE.EPOCH_NO + ON CONFLICT ( + POOL_ID, + EPOCH_NO + ) DO UPDATE + SET AMOUNT = EXCLUDED.AMOUNT + WHERE POOL_ACTIVE_STAKE_CACHE.AMOUNT IS DISTINCT FROM EXCLUDED.AMOUNT; + + /* EPOCH ACTIVE STAKE CACHE */ + SELECT + COALESCE(MAX(epoch_no), 0) + INTO _last_epoch_active_stake_cache_epoch_no + FROM + GREST.EPOCH_ACTIVE_STAKE_CACHE; + + INSERT INTO GREST.EPOCH_ACTIVE_STAKE_CACHE + SELECT + EPOCH_STAKE.EPOCH_NO, + SUM(EPOCH_STAKE.AMOUNT) AS AMOUNT + FROM + PUBLIC.EPOCH_STAKE + WHERE + EPOCH_STAKE.EPOCH_NO >= _last_epoch_active_stake_cache_epoch_no + AND + EPOCH_STAKE.EPOCH_NO <= _epoch_no + GROUP BY + EPOCH_STAKE.EPOCH_NO + ON CONFLICT ( + EPOCH_NO + ) DO UPDATE + SET AMOUNT = EXCLUDED.AMOUNT + WHERE EPOCH_ACTIVE_STAKE_CACHE.AMOUNT IS DISTINCT FROM EXCLUDED.AMOUNT; + + /* ACCOUNT ACTIVE STAKE CACHE */ + SELECT + COALESCE(MAX(epoch_no), (_epoch_no - 4) ) + INTO _last_account_active_stake_cache_epoch_no + FROM + GREST.ACCOUNT_ACTIVE_STAKE_CACHE; + + INSERT INTO GREST.ACCOUNT_ACTIVE_STAKE_CACHE + SELECT + STAKE_ADDRESS.VIEW AS STAKE_ADDRESS, + POOL_HASH.VIEW AS POOL_ID, + EPOCH_STAKE.EPOCH_NO AS EPOCH_NO, + SUM(EPOCH_STAKE.AMOUNT) AS AMOUNT + FROM + PUBLIC.EPOCH_STAKE + INNER JOIN PUBLIC.POOL_HASH ON POOL_HASH.ID = EPOCH_STAKE.POOL_ID + INNER JOIN PUBLIC.STAKE_ADDRESS ON STAKE_ADDRESS.ID = EPOCH_STAKE.ADDR_ID + WHERE + EPOCH_STAKE.EPOCH_NO > _last_account_active_stake_cache_epoch_no + AND + EPOCH_STAKE.EPOCH_NO <= _epoch_no + GROUP BY + STAKE_ADDRESS.ID, + POOL_HASH.ID, + EPOCH_STAKE.EPOCH_NO + ON CONFLICT ( + STAKE_ADDRESS, + POOL_ID, + EPOCH_NO + ) DO UPDATE + SET AMOUNT = EXCLUDED.AMOUNT; + + DELETE FROM GREST.ACCOUNT_ACTIVE_STAKE_CACHE + WHERE EPOCH_NO <= (_epoch_no - 4); + + /* CONTROL TABLE ENTRY */ + PERFORM grest.update_control_table( + 'last_active_stake_validated_epoch', + _epoch_no::text + ); + END; +$$; + +COMMENT ON FUNCTION grest.active_stake_cache_update + IS 'Internal function to update active stake cache (epoch, pool, and account tables).'; diff --git a/files/grest/rpc/01_cached_tables/asset_registry_cache.sql b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql new file mode 100644 index 00000000..e54d23d4 --- /dev/null +++ b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql @@ -0,0 +1,59 @@ +DROP TABLE IF EXISTS grest.asset_registry_cache; + +CREATE TABLE grest.asset_registry_cache ( + asset_policy text NOT NULL, + asset_name text NOT NULL, + name text NOT NULL, + description text NOT NULL, + ticker text, + url text, + logo text, + decimals integer +); + +CREATE UNIQUE INDEX IF NOT EXISTS idx_asset ON grest.asset_registry_cache (asset_policy, asset_name); + +CREATE FUNCTION grest.asset_registry_cache_update ( + _asset_policy text, + _asset_name text, + _name text, + _description text, + _ticker text DEFAULT NULL, + _url text DEFAULT NULL, + _logo text DEFAULT NULL, + _decimals word31type DEFAULT 0 + ) + RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + INSERT INTO grest.asset_registry_cache ( + asset_policy, + asset_name, + name, + description, + ticker, + url, + logo, + decimals + ) + VALUES( + _asset_policy, + _asset_name, + _name, + _description, + _ticker, + _url, + _logo, + _decimals + ) + ON CONFLICT (asset_policy, asset_name) + DO UPDATE SET + name = _name, + description = _description, + ticker = _ticker, + url = _url, + logo = _logo, + decimals = _decimals; +END; +$$; \ No newline at end of file diff --git a/files/grest/rpc/01_cached_tables/epoch_info_cache.sql b/files/grest/rpc/01_cached_tables/epoch_info_cache.sql new file mode 100644 index 00000000..fca31a91 --- /dev/null +++ b/files/grest/rpc/01_cached_tables/epoch_info_cache.sql @@ -0,0 +1,290 @@ +CREATE TABLE IF NOT EXISTS grest.epoch_info_cache ( + epoch_no word31type PRIMARY KEY NOT NULL, + i_out_sum word128type, + i_fees lovelace, + i_tx_count word31type, + i_blk_count word31type, + i_first_block_time numeric UNIQUE, + i_last_block_time numeric UNIQUE, + i_total_rewards lovelace, + i_avg_blk_reward lovelace, + i_last_tx_id bigint, + p_min_fee_a word31type, + p_min_fee_b word31type, + p_max_block_size word31type, + p_max_tx_size word31type, + p_max_bh_size word31type, + p_key_deposit lovelace, + p_pool_deposit lovelace, + p_max_epoch word31type, + p_optimal_pool_count word31type, + p_influence double precision, + p_monetary_expand_rate double precision, + p_treasury_growth_rate double precision, + p_decentralisation double precision, + p_extra_entropy text, + p_protocol_major word31type, + p_protocol_minor word31type, + p_min_utxo_value lovelace, + p_min_pool_cost lovelace, + p_nonce text, + p_block_hash text, + p_cost_models character varying, + p_price_mem double precision, + p_price_step double precision, + p_max_tx_ex_mem word64type, + p_max_tx_ex_steps word64type, + p_max_block_ex_mem word64type, + p_max_block_ex_steps word64type, + p_max_val_size word64type, + p_collateral_percent word31type, + p_max_collateral_inputs word31type, + p_coins_per_utxo_size lovelace +); + +COMMENT ON TABLE grest.epoch_info_cache IS 'Contains detailed info for epochs including protocol parameters'; + +CREATE FUNCTION grest.EPOCH_INFO_CACHE_UPDATE ( + _epoch_no_to_insert_from bigint default NULL + ) + RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + _curr_epoch bigint; + _latest_epoch_no_in_cache bigint; +BEGIN + -- Check previous cache update completed before running + IF ( + SELECT + COUNT(pid) > 1 + FROM + pg_stat_activity + WHERE + state = 'active' AND query ILIKE '%GREST.EPOCH_INFO_CACHE_UPDATE%' + AND datname = (SELECT current_database()) + ) THEN + RAISE EXCEPTION 'Previous EPOCH_INFO_CACHE_UPDATE query still running but should have completed! Exiting...'; + END IF; + + -- GREST control table entry + PERFORM grest.update_control_table( + 'epoch_info_cache_last_updated', + (now() at time zone 'utc')::text + ); + + SELECT + MAX(no) INTO _curr_epoch + FROM + public.epoch; + + IF _epoch_no_to_insert_from IS NULL THEN + SELECT + COALESCE(MAX(epoch_no), 0) INTO _latest_epoch_no_in_cache + FROM + grest.epoch_info_cache + WHERE + i_first_block_time IS NOT NULL; + + IF _latest_epoch_no_in_cache = 0 THEN + RAISE NOTICE 'Epoch info cache table is empty, starting initial population...'; + PERFORM grest.EPOCH_INFO_CACHE_UPDATE (0); + RETURN; + END IF; + + RAISE NOTICE 'Latest epoch in cache: %, current epoch: %.', _latest_epoch_no_in_cache, _curr_epoch; + + IF _curr_epoch = _latest_epoch_no_in_cache THEN + RAISE NOTICE 'Updating latest epoch info in cache...'; + PERFORM grest.UPDATE_LATEST_EPOCH_INFO_CACHE(_curr_epoch, _latest_epoch_no_in_cache); + RETURN; + END IF; + + IF _latest_epoch_no_in_cache > _curr_epoch THEN + RAISE NOTICE 'No update needed, exiting...'; + RETURN; + END IF; + + RAISE NOTICE 'Updating cache with new epoch(s) data...'; + -- We need to update last epoch one last time before going to new one + PERFORM grest.UPDATE_LATEST_EPOCH_INFO_CACHE(_curr_epoch, _latest_epoch_no_in_cache); + -- Populate rewards data for epoch n - 2 + PERFORM grest.UPDATE_TOTAL_REWARDS_EPOCH_INFO_CACHE(_latest_epoch_no_in_cache - 1); + -- Continue new epoch data insert + _epoch_no_to_insert_from := _latest_epoch_no_in_cache + 1; + END IF; + + RAISE NOTICE 'Deleting cache records from epoch % onwards...', _epoch_no_to_insert_from; + DELETE FROM grest.epoch_info_cache + WHERE epoch_no >= _epoch_no_to_insert_from; + + INSERT INTO grest.epoch_info_cache + SELECT DISTINCT ON (b.time) + e.no AS epoch_no, + e.out_sum AS i_out_sum, + e.fees AS i_fees, + e.tx_count AS i_tx_count, + e.blk_count AS i_blk_count, + EXTRACT(epoch from e.start_time) AS i_first_block_time, + EXTRACT(epoch from e.end_time) AS i_last_block_time, + CASE -- populated in epoch n + 2 + WHEN e.no <= _curr_epoch - 2 THEN reward_pot.amount + ELSE NULL + END AS i_total_rewards, + CASE -- populated in epoch n + 2 + WHEN e.no <= _curr_epoch THEN ROUND(reward_pot.amount / e.blk_count) + ELSE NULL + END AS i_avg_blk_reward, + last_tx.tx_id AS i_last_tx_id, + ep.min_fee_a AS p_min_fee_a, + ep.min_fee_b AS p_min_fee_b, + ep.max_block_size AS p_max_block_size, + ep.max_tx_size AS p_max_tx_size, + ep.max_bh_size AS p_max_bh_size, + ep.key_deposit AS p_key_deposit, + ep.pool_deposit AS p_pool_deposit, + ep.max_epoch AS p_max_epoch, + ep.optimal_pool_count AS p_optimal_pool_count, + ep.influence AS p_influence, + ep.monetary_expand_rate AS p_monetary_expand_rate, + ep.treasury_growth_rate AS p_treasury_growth_rate, + ep.decentralisation AS p_decentralisation, + ENCODE(ep.extra_entropy, 'hex') AS p_extra_entropy, + ep.protocol_major AS p_protocol_major, + ep.protocol_minor AS p_protocol_minor, + ep.min_utxo_value AS p_min_utxo_value, + ep.min_pool_cost AS p_min_pool_cost, + ENCODE(ep.nonce, 'hex') AS p_nonce, + ENCODE(b.hash, 'hex') AS p_block_hash, + cm.costs AS p_cost_models, + ep.price_mem AS p_price_mem, + ep.price_step AS p_price_step, + ep.max_tx_ex_mem AS p_max_tx_ex_mem, + ep.max_tx_ex_steps AS p_max_tx_ex_steps, + ep.max_block_ex_mem AS p_max_block_ex_mem, + ep.max_block_ex_steps AS p_max_block_ex_steps, + ep.max_val_size AS p_max_val_size, + ep.collateral_percent AS p_collateral_percent, + ep.max_collateral_inputs AS p_max_collateral_inputs, + ep.coins_per_utxo_size AS p_coins_per_utxo_size + FROM + epoch e + LEFT JOIN epoch_param ep ON ep.epoch_no = e.no + LEFT JOIN cost_model cm ON cm.id = ep.cost_model_id + INNER JOIN block b ON b.time = e.start_time + LEFT JOIN LATERAL ( + SELECT + e.no, + SUM(r.amount) as amount + FROM + reward r + WHERE + r.earned_epoch = e.no + GROUP BY + e.no + ) reward_pot ON TRUE + LEFT JOIN LATERAL ( + SELECT + MAX(tx.id) AS tx_id + FROM + block b + INNER JOIN tx ON tx.block_id = b.id + WHERE + b.epoch_no <= e.no + AND b.block_no IS NOT NULL + AND b.tx_count != 0 + ) last_tx ON TRUE + WHERE + e.no >= _epoch_no_to_insert_from + ORDER BY + b.time ASC, + b.id ASC, + e.no ASC; +END; +$$; + +-- Helper function for updating current epoch data +CREATE FUNCTION grest.UPDATE_LATEST_EPOCH_INFO_CACHE (_curr_epoch bigint, _epoch_no_to_update bigint) + RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + -- only update last tx id in case of new epoch + IF _curr_epoch <> _epoch_no_to_update THEN + UPDATE + grest.epoch_info_cache + SET + i_last_tx_id = last_tx.tx_id + FROM ( + SELECT + MAX(tx.id) AS tx_id + FROM + block b + INNER JOIN tx ON tx.block_id = b.id + WHERE + b.epoch_no <= _epoch_no_to_update + AND b.block_no IS NOT NULL + AND b.tx_count != 0 + ) last_tx + WHERE epoch_no = _epoch_no_to_update; + END IF; + + UPDATE + grest.epoch_info_cache + SET + i_out_sum = update_table.out_sum, + i_fees = update_table.fees, + i_tx_count = update_table.tx_count, + i_blk_count = update_table.blk_count, + i_last_block_time = EXTRACT(epoch from update_table.end_time) + FROM ( + SELECT + e.out_sum, + e.fees, + e.tx_count, + e.blk_count, + e.end_time + FROM + epoch e + WHERE + e.no = _epoch_no_to_update + ) update_table + WHERE + epoch_no = _epoch_no_to_update; +END; +$$; + +-- Helper function for updating epoch total rewards (epoch n - 2) +CREATE FUNCTION grest.UPDATE_TOTAL_REWARDS_EPOCH_INFO_CACHE (_epoch_no_to_update bigint) + RETURNS void + LANGUAGE plpgsql + AS $$ +BEGIN + UPDATE + grest.epoch_info_cache + SET + i_total_rewards = update_t.amount, + i_avg_blk_reward = update_t.avg_blk_reward + FROM ( + SELECT + reward_pot.amount, + ROUND(reward_pot.amount / e.blk_count) AS avg_blk_reward + FROM ( + SELECT + r.earned_epoch, + SUM(r.amount) AS amount + FROM + reward r + WHERE + r.earned_epoch = _epoch_no_to_update + GROUP BY + r.earned_epoch + ) reward_pot + INNER JOIN epoch e ON reward_pot.earned_epoch = e.no + AND e.no = _epoch_no_to_update + ) update_t + WHERE + epoch_no = _epoch_no_to_update; +END; +$$; + diff --git a/files/grest/rpc/01_cached_tables/pool_history_cache.sql b/files/grest/rpc/01_cached_tables/pool_history_cache.sql new file mode 100644 index 00000000..e065ca18 --- /dev/null +++ b/files/grest/rpc/01_cached_tables/pool_history_cache.sql @@ -0,0 +1,260 @@ +drop table if exists grest.pool_history_cache; + +CREATE TABLE grest.pool_history_cache ( + pool_id varchar, + epoch_no int8 NULL, + active_stake lovelace NULL, + active_stake_pct numeric NULL, + saturation_pct numeric NULL, + block_cnt int8 NULL, + delegator_cnt int8 NULL, + pool_fee_variable float8 NULL, + pool_fee_fixed lovelace NULL, + pool_fees float8 NULL, + deleg_rewards float8 NULL, + epoch_ros numeric NULL, + PRIMARY KEY (pool_id, epoch_no) +); + +COMMENT ON TABLE grest.pool_history_cache IS 'A history of pool performance including blocks, delegators, active stake, fees and rewards'; + +create function grest.pool_history_cache_update (_epoch_no_to_insert_from bigint default NULL) + returns void + language plpgsql + as $$ +declare + _curr_epoch bigint; + _latest_epoch_no_in_cache bigint; +begin + IF ( + SELECT + COUNT(pid) > 1 + FROM + pg_stat_activity + WHERE + state = 'active' AND query ILIKE '%GREST.pool_history_cache_update%' + AND datname = (SELECT current_database()) + ) THEN + RAISE EXCEPTION 'Previous pool_history_cache_update query still running but should have completed! Exiting...'; + END IF; + + INSERT INTO GREST.CONTROL_TABLE (key, last_value) + values('pool_history_cache_last_updated', now() at time zone 'utc') + ON CONFLICT (key) + DO UPDATE SET last_value = now() at time zone 'utc'; + + if _epoch_no_to_insert_from is null then + select + COALESCE(MAX(epoch_no), 0) into _latest_epoch_no_in_cache + from + grest.pool_history_cache; + if _latest_epoch_no_in_cache = 0 then + RAISE NOTICE 'Pool history cache table is empty, starting initial population...'; + PERFORM grest.pool_history_cache_update (0); + return; + end if; + select + MAX(no) into _curr_epoch + from + epoch; + -- no-op if we already have data up until second most recent epoch + if _latest_epoch_no_in_cache >= (_curr_epoch - 1) then + return; + end if; + -- if current epoch is at least 2 ahead of latest in cache, repopulate from latest in cache until current-1 + _epoch_no_to_insert_from := _latest_epoch_no_in_cache; + end if; + -- purge the data for the given epoch range, in theory should do nothing if invoked only at start of new epoch + delete from grest.pool_history_cache + where epoch_no >= _epoch_no_to_insert_from; + insert into grest.pool_history_cache ( with blockcounts as ( + select + sl.pool_hash_id, + b.epoch_no, + COUNT(*) as block_cnt + from + block b, + slot_leader sl + where + b.slot_leader_id = sl.id + and epoch_no >= _epoch_no_to_insert_from + group by + sl.pool_hash_id, + b.epoch_no), + leadertotals as ( + select + pool_id, + earned_epoch, + COALESCE(SUM(amount), 0) as leadertotal + from + reward r + where + r.type = 'leader' + and earned_epoch >= _epoch_no_to_insert_from + group by + pool_id, + earned_epoch), + membertotals as ( + select + pool_id, + earned_epoch, + COALESCE(SUM(amount), 0) as memtotal + from + reward r + where + r.type = 'member' + and earned_epoch >= _epoch_no_to_insert_from + group by + pool_id, + earned_epoch), + activeandfees as ( + select + pool_id, + epoch_no, + amount as active_stake, + ( + select + margin + from + pool_update + where + id = ( + select + MAX(pup2.id) + from + pool_hash ph, + pool_update pup2 + where + pup2.hash_id = ph.id + and ph.view = act.pool_id + and pup2.active_epoch_no <= act.epoch_no)) pool_fee_variable, + ( + select + fixed_cost + from + pool_update + where + id = ( + select + MAX(pup2.id) + from + pool_update pup2, + pool_hash ph + where + ph.view = act.pool_id + and pup2.hash_id = ph.id + and pup2.active_epoch_no <= act.epoch_no)) pool_fee_fixed, + (amount / ( + select + NULLIF (amount, 0) + from + grest.EPOCH_ACTIVE_STAKE_CACHE epochActiveStakeCache + where + epochActiveStakeCache.epoch_no = act.epoch_no)) * 100 active_stake_pct, + ROUND((amount / ( + select + supply::bigint / ( + select + p_optimal_pool_count from grest.epoch_info_cache + where + epoch_no = act.epoch_no) + from grest.totals (act.epoch_no)) * 100), 2) saturation_pct + from + grest.pool_active_stake_cache act + where + epoch_no >= _epoch_no_to_insert_from + -- TODO: revisit later: currently ignore latest epoch as active stake might not be populated for it yet + and epoch_no < ( + select + MAX(e."no") + from + epoch e)), + delegators as ( + select + pool_id, + epoch_no, + COUNT(*) as delegator_cnt + from + epoch_stake es + where + epoch_no >= _epoch_no_to_insert_from + group by + pool_id, + epoch_no +) + select + ph.view as pool_id, + actf.epoch_no, + actf.active_stake, + actf.active_stake_pct, + actf.saturation_pct, + COALESCE(b.block_cnt, 0) as block_cnt, + del.delegator_cnt, + actf.pool_fee_variable, + actf.pool_fee_fixed, + -- for debugging: m.memtotal, + -- for debugging: l.leadertotal, + case COALESCE(b.block_cnt, 0) + when 0 then + 0 + else + -- special case for when reward information is not available yet + case COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) + when 0 then + null + else + case + when COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed then COALESCE(l.leadertotal, 0) + else ROUND(actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable)) + end + end + end pool_fees, + case COALESCE(b.block_cnt, 0) + when 0 then + 0 + else + -- special case for when reward information is not available yet + case COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) + when 0 then + null + else + case + when COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed then COALESCE(m.memtotal, 0) + else ROUND(COALESCE(m.memtotal, 0) + (COALESCE(l.leadertotal, 0) - (actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable)))) + end + end + end deleg_rewards, + case COALESCE(b.block_cnt, 0) + when 0 then + 0 + else + -- special case for when reward information is not available yet + case COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) + when 0 then + null + else + case + when COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed then + ROUND((((POW(( LEAST(( (COALESCE(m.memtotal, 0)) / (NULLIF(actf.active_stake,0)) ), 1000) + 1), 73) - 1)) * 100)::numeric, 9) + -- using LEAST as a way to prevent overflow, in case of dodgy database data (e.g. giant rewards / tiny active stake) + else ROUND((((POW(( LEAST(( ((COALESCE(m.memtotal, 0) + (COALESCE(l.leadertotal, 0) - (actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable))))) / (NULLIF(actf.active_stake,0)) ), 1000) + 1), 73) - 1)) * 100)::numeric, 9) + end + end + end epoch_ros + from + pool_hash ph + inner join activeandfees actf on actf.pool_id = ph."view" + left join blockcounts b on ph.id = b.pool_hash_id + and actf.epoch_no = b.epoch_no + left join leadertotals l on ph.id = l.pool_id + and actf.epoch_no = l.earned_epoch + left join membertotals m on ph.id = m.pool_id + and actf.epoch_no = m.earned_epoch + left join delegators del on ph.id = del.pool_id + and actf.epoch_no = del.epoch_no); +end; +$$; + +COMMENT ON FUNCTION grest.pool_history_cache_update IS 'Internal function to update pool history for data from specified epoch until +current-epoch-minus-one. Invoke with non-empty param for initial population, with empty for subsequent updates'; diff --git a/files/grest/rpc/01_cached_tables/pool_info_cache.sql b/files/grest/rpc/01_cached_tables/pool_info_cache.sql new file mode 100644 index 00000000..b3726644 --- /dev/null +++ b/files/grest/rpc/01_cached_tables/pool_info_cache.sql @@ -0,0 +1,340 @@ +DROP TABLE IF EXISTS grest.pool_info_cache; + +CREATE TABLE grest.pool_info_cache ( + id SERIAL PRIMARY KEY, + tx_id bigint NOT NULL, + update_id bigint NOT NULL, + tx_hash text, + block_time numeric, + pool_hash_id bigint NOT NULL, + pool_id_bech32 character varying NOT NULL, + pool_id_hex text NOT NULL, + active_epoch_no bigint NOT NULL, + vrf_key_hash text NOT NULL, + margin double precision NOT NULL, + fixed_cost lovelace NOT NULL, + pledge lovelace NOT NULL, + reward_addr character varying, + owners character varying [], + relays jsonb [], + meta_id bigint, + meta_url character varying, + meta_hash text, + pool_status text, + retiring_epoch word31type +); + +COMMENT ON TABLE grest.pool_info_cache IS 'A summary of all pool parameters and updates'; + +CREATE FUNCTION grest.pool_info_insert ( + _update_id bigint, + _tx_id bigint, + _hash_id bigint, + _active_epoch_no bigint, + _vrf_key_hash hash32type, + _margin double precision, + _fixed_cost lovelace, + _pledge lovelace, + _reward_addr_id bigint, + _meta_id bigint + ) + RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + _current_epoch_no word31type; + _retiring_epoch word31type; + _pool_status text; +BEGIN + SELECT COALESCE(MAX(no), 0) INTO _current_epoch_no FROM public.epoch; + + SELECT pr.retiring_epoch INTO _retiring_epoch + FROM public.pool_retire AS pr + WHERE pr.hash_id = _hash_id + AND pr.announced_tx_id > _tx_id + ORDER BY pr.id + LIMIT 1; + + IF _retiring_epoch IS NULL THEN + _pool_status := 'registered'; + ELSIF _retiring_epoch > _current_epoch_no THEN + _pool_status := 'retiring'; + ELSE + _pool_status := 'retired'; + END IF; + + INSERT INTO grest.pool_info_cache ( + tx_id, + update_id, + tx_hash, + block_time, + pool_hash_id, + pool_id_bech32, + pool_id_hex, + active_epoch_no, + vrf_key_hash, + margin, + fixed_cost, + pledge, + reward_addr, + owners, + relays, + meta_id, + meta_url, + meta_hash, + pool_status, + retiring_epoch + ) + SELECT + _tx_id, + _update_id, + encode(tx.hash::bytea, 'hex'), + EXTRACT(epoch from b.time), + _hash_id, + ph.view, + encode(ph.hash_raw::bytea, 'hex'), + _active_epoch_no, + encode(_vrf_key_hash::bytea, 'hex'), + _margin, + _fixed_cost, + _pledge, + sa.view, + ARRAY( + SELECT + sa.view + FROM public.pool_owner AS po + INNER JOIN public.stake_address AS sa ON sa.id = po.addr_id + WHERE po.pool_update_id = _update_id + ), + ARRAY( + SELECT json_build_object( + 'ipv4', pr.ipv4, + 'ipv6', pr.ipv6, + 'dns', pr.dns_name, + 'srv', pr.dns_srv_name, + 'port', pr.port + ) relay + FROM public.pool_relay AS pr + WHERE pr.update_id = _update_id + ), + _meta_id, + pmr.url, + encode(pmr.hash::bytea, 'hex'), + _pool_status, + _retiring_epoch + FROM public.pool_hash AS ph + INNER JOIN public.tx ON tx.id = _tx_id + INNER JOIN public.block AS b ON b.id = tx.block_id + LEFT JOIN public.pool_metadata_ref AS pmr ON pmr.id = _meta_id + LEFT JOIN public.stake_address AS sa ON sa.id = _reward_addr_id + WHERE ph.id = _hash_id; +END; +$$; + +COMMENT ON FUNCTION grest.pool_info_insert IS 'Internal function to insert a single pool update'; + +CREATE FUNCTION grest.pool_info_retire_status () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +BEGIN + UPDATE grest.pool_info_cache + SET + pool_status = 'retired' + WHERE + pool_status = 'retiring' + AND + retiring_epoch <= NEW.no; + RETURN NULL; +END; +$$; + +COMMENT ON FUNCTION grest.pool_info_retire_status IS 'Internal function to update pool_info_cache with new retire status based on epoch switch'; + +CREATE FUNCTION grest.pool_info_retire_update () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +DECLARE + _current_epoch_no word31type; + _pool_hash_id bigint; + _latest_pool_update_tx_id bigint; + _retiring_epoch word31type; + _pool_status text; +BEGIN + SELECT COALESCE(MAX(no), 0) INTO _current_epoch_no FROM public.epoch; + + IF (TG_OP = 'DELETE') THEN + _pool_hash_id := OLD.hash_id; + ELSIF (TG_OP = 'INSERT') THEN + _pool_hash_id := NEW.hash_id; + END IF; + SELECT COALESCE(MAX(tx_id), 0) INTO _latest_pool_update_tx_id FROM grest.pool_info_cache AS pic WHERE pic.pool_hash_id = _pool_hash_id; + + SELECT pr.retiring_epoch INTO _retiring_epoch + FROM public.pool_retire AS pr + WHERE pr.hash_id = _pool_hash_id + AND pr.announced_tx_id > _latest_pool_update_tx_id + ORDER BY pr.id + LIMIT 1; + + IF _retiring_epoch IS NULL THEN + _pool_status := 'registered'; + ELSIF _retiring_epoch > _current_epoch_no THEN + _pool_status := 'retiring'; + ELSE + _pool_status := 'retired'; + END IF; + + UPDATE grest.pool_info_cache + SET + pool_status = _pool_status, + retiring_epoch = _retiring_epoch + WHERE + pool_hash_id = _pool_hash_id + AND + tx_id = _latest_pool_update_tx_id; + + RETURN NULL; +END; +$$; + +COMMENT ON FUNCTION grest.pool_info_retire_update IS 'Internal function to update pool_info cache table based on insert/delete on pool_retire table'; + +CREATE FUNCTION grest.pool_info_update () + RETURNS TRIGGER + LANGUAGE plpgsql + AS $$ +DECLARE + _latest_pool_update_id integer; +BEGIN + IF (TG_TABLE_NAME = 'pool_update') THEN + IF (TG_OP = 'INSERT') THEN + PERFORM grest.pool_info_insert( + NEW.id, + NEW.registered_tx_id, + NEW.hash_id, + NEW.active_epoch_no, + NEW.vrf_key_hash, + NEW.margin, + NEW.fixed_cost, + NEW.pledge, + NEW.reward_addr_id, + NEW.meta_id + ); + ELSIF (TG_OP = 'DELETE') THEN + DELETE FROM grest.pool_info_cache + WHERE tx_id = OLD.registered_tx_id; + END IF; + + ELSIF (TG_TABLE_NAME = 'pool_relay') THEN + SELECT pic.id INTO _latest_pool_update_id + FROM grest.pool_info_cache AS pic + INNER JOIN public.pool_update AS pu ON pu.hash_id = pic.pool_hash_id AND pu.registered_tx_id = pic.tx_id + WHERE pu.id = NEW.update_id; + + IF (_latest_pool_update_id IS NULL) THEN + RETURN NULL; + END IF; + + UPDATE grest.pool_info_cache + SET + relays = relays || jsonb_build_object ( + 'ipv4', NEW.ipv4, + 'ipv6', NEW.ipv6, + 'dns', NEW.dns_name, + 'srv', NEW.dns_srv_name, + 'port', NEW.port + ) + WHERE + id = _latest_pool_update_id; + + ELSIF (TG_TABLE_NAME = 'pool_owner') THEN + UPDATE grest.pool_info_cache + SET + owners = owners || (SELECT sa.view FROM public.stake_address AS sa WHERE sa.id = NEW.addr_id) + WHERE + update_id = NEW.pool_update_id; + END IF; + + RETURN NULL; +END; +$$; + +COMMENT ON FUNCTION grest.pool_info_update IS 'Internal function to insert/delete pool updates in pool_info cache table'; + + +-- Create pool_info_cache trigger based on new/deleted pool updates +DROP TRIGGER IF EXISTS pool_info_update_trigger ON public.pool_update; + +CREATE TRIGGER pool_info_update_trigger + AFTER INSERT OR DELETE ON public.pool_update + FOR EACH ROW + EXECUTE FUNCTION grest.pool_info_update (); + +-- Create pool_info_cache trigger based on new entry in pool_relay +DROP TRIGGER IF EXISTS pool_info_update_trigger ON public.pool_relay; + +CREATE TRIGGER pool_info_update_trigger + AFTER INSERT ON public.pool_relay + FOR EACH ROW + EXECUTE FUNCTION grest.pool_info_update (); + +-- Create pool_info_cache trigger based on new entry in pool_owner +DROP TRIGGER IF EXISTS pool_info_update_trigger ON public.pool_owner; + +CREATE TRIGGER pool_info_update_trigger + AFTER INSERT ON public.pool_owner + FOR EACH ROW + EXECUTE FUNCTION grest.pool_info_update (); + +-- Create pool_info_cache trigger based on new/deleted pool retire entries +DROP TRIGGER IF EXISTS pool_info_retire_update_trigger ON public.pool_retire; + +CREATE TRIGGER pool_info_retire_update_trigger + AFTER INSERT OR DELETE ON public.pool_retire + FOR EACH ROW + EXECUTE FUNCTION grest.pool_info_retire_update (); + +-- Create pool_info_cache trigger based on new epoch for retire status update +DROP TRIGGER IF EXISTS pool_info_retire_status_trigger ON public.epoch; + +CREATE TRIGGER pool_info_retire_status_trigger + AFTER INSERT ON public.epoch + FOR EACH ROW + EXECUTE FUNCTION grest.pool_info_retire_status (); + + +-- Initialize pool_info_cache table with all current pool data +DO $$ +DECLARE + _latest_pool_info_tx_id bigint; + rec RECORD; +BEGIN + SELECT COALESCE(MAX(tx_id), 0) INTO _latest_pool_info_tx_id FROM grest.pool_info_cache; + + FOR rec IN ( + SELECT * FROM public.pool_update AS pu WHERE pu.registered_tx_id > _latest_pool_info_tx_id + ) LOOP + PERFORM grest.pool_info_insert( + rec.id, + rec.registered_tx_id, + rec.hash_id, + rec.active_epoch_no, + rec.vrf_key_hash, + rec.margin, + rec.fixed_cost, + rec.pledge, + rec.reward_addr_id, + rec.meta_id + ); + END LOOP; + + CREATE INDEX IF NOT EXISTS idx_tx_id ON grest.pool_info_cache (tx_id); + CREATE INDEX IF NOT EXISTS idx_pool_id_bech32 ON grest.pool_info_cache (pool_id_bech32); + CREATE INDEX IF NOT EXISTS idx_pool_hash_id ON grest.pool_info_cache (pool_hash_id); + CREATE INDEX IF NOT EXISTS idx_pool_status ON grest.pool_info_cache (pool_status); + CREATE INDEX IF NOT EXISTS idx_meta_id ON grest.pool_info_cache (meta_id); +END; +$$; + diff --git a/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql b/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql new file mode 100644 index 00000000..4f33942e --- /dev/null +++ b/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql @@ -0,0 +1,249 @@ +CREATE TABLE IF NOT EXISTS GREST.STAKE_DISTRIBUTION_CACHE ( + STAKE_ADDRESS varchar PRIMARY KEY, + POOL_ID varchar, + TOTAL_BALANCE numeric, + UTXO numeric, + REWARDS numeric, + WITHDRAWALS numeric, + REWARDS_AVAILABLE numeric +); + +CREATE OR REPLACE PROCEDURE GREST.UPDATE_STAKE_DISTRIBUTION_CACHE () LANGUAGE PLPGSQL AS $$ +DECLARE -- Last block height to control future re-runs of the query + _last_accounted_block_height bigint; + _last_account_tx_id bigint; + _active_stake_epoch bigint; + _last_active_stake_blockid bigint; + _latest_epoch bigint; +BEGIN + + SELECT MAX(block_no) FROM PUBLIC.BLOCK + WHERE block_no IS NOT NULL INTO _last_accounted_block_height; + + SELECT (last_value::integer - 2)::integer INTO _active_stake_epoch FROM GREST.CONTROL_TABLE + WHERE key = 'last_active_stake_validated_epoch'; + + SELECT MAX(tx.id) INTO _last_account_tx_id + FROM PUBLIC.TX + INNER JOIN BLOCK b ON b.id = tx.block_id + WHERE b.epoch_no <= _active_stake_epoch + AND b.block_no IS NOT NULL + AND b.tx_count != 0; + + SELECT MAX(no) INTO _latest_epoch FROM PUBLIC.EPOCH WHERE NO IS NOT NULL; + + WITH + accounts_with_delegated_pools AS ( + SELECT DISTINCT ON (STAKE_ADDRESS.ID) stake_address.id as stake_address_id, stake_address.view as stake_address, pool_hash_id + FROM STAKE_ADDRESS + INNER JOIN DELEGATION ON DELEGATION.ADDR_ID = STAKE_ADDRESS.ID + WHERE + NOT EXISTS ( + SELECT TRUE FROM DELEGATION D + WHERE D.ADDR_ID = DELEGATION.ADDR_ID AND D.ID > DELEGATION.ID + ) + AND NOT EXISTS ( + SELECT TRUE FROM STAKE_DEREGISTRATION + WHERE STAKE_DEREGISTRATION.ADDR_ID = DELEGATION.ADDR_ID + AND STAKE_DEREGISTRATION.TX_ID > DELEGATION.TX_ID + ) + -- Account must be present in epoch_stake table for the last validated epoch + AND EXISTS ( + SELECT TRUE FROM EPOCH_STAKE + WHERE EPOCH_STAKE.EPOCH_NO = ( + SELECT last_value::integer FROM GREST.CONTROL_TABLE + WHERE key = 'last_active_stake_validated_epoch' + ) + AND EPOCH_STAKE.ADDR_ID = STAKE_ADDRESS.ID + ) + ), + pool_ids as ( + SELECT awdp.stake_address_id, + pool_hash.view AS pool_id + FROM POOL_HASH + INNER JOIN accounts_with_delegated_pools awdp ON awdp.pool_hash_id = pool_hash.id + ), + account_active_stake AS ( + SELECT awdp.stake_address_id, acsc.amount + FROM grest.account_active_stake_cache acsc + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address = acsc.stake_address + WHERE epoch_no = (_active_stake_epoch + 2) + ), + account_delta_tx_ins AS ( + SELECT awdp.stake_address_id, tx_in.tx_out_id AS txoid, tx_in.tx_out_index AS txoidx FROM tx_in + LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE tx_in.tx_in_id > _last_account_tx_id + ), + account_delta_input AS ( + SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + FROM account_delta_tx_ins + LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id + GROUP BY tx_out.stake_address_id + ), + account_delta_output AS ( + SELECT awdp.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + FROM tx_out + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE TX_OUT.TX_ID > _last_account_tx_id + GROUP BY awdp.stake_address_id + ), + account_delta_rewards AS ( + SELECT awdp.stake_address_id, COALESCE(SUM(reward.amount), 0) AS REWARDS + FROM REWARD + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = reward.addr_id + WHERE + ( REWARD.SPENDABLE_EPOCH >= (_active_stake_epoch + 2) AND REWARD.SPENDABLE_EPOCH <= _latest_epoch ) + OR ( REWARD.TYPE = 'refund' AND REWARD.SPENDABLE_EPOCH >= (_active_stake_epoch + 1) AND REWARD.SPENDABLE_EPOCH <= _latest_epoch ) + GROUP BY awdp.stake_address_id + ), + account_delta_withdrawals AS ( + SELECT accounts_with_delegated_pools.stake_address_id, COALESCE(SUM(withdrawal.amount), 0) AS withdrawals + FROM withdrawal + INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = withdrawal.addr_id + WHERE withdrawal.tx_id > _last_account_tx_id + GROUP BY accounts_with_delegated_pools.stake_address_id + ), + account_total_rewards as ( + SELECT accounts_with_delegated_pools.stake_address_id, + COALESCE(SUM(REWARD.AMOUNT), 0) AS REWARDS + FROM REWARD + INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = reward.addr_id + WHERE REWARD.SPENDABLE_EPOCH <= _latest_epoch + GROUP BY accounts_with_delegated_pools.stake_address_id + ), + account_total_withdrawals as ( + SELECT accounts_with_delegated_pools.stake_address_id, + COALESCE(SUM(WITHDRAWAL.AMOUNT), 0) AS WITHDRAWALS + FROM WITHDRAWAL + INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = WITHDRAWAL.addr_id + GROUP BY accounts_with_delegated_pools.stake_address_id + ) + + -- INSERT QUERY START + INSERT INTO GREST.STAKE_DISTRIBUTION_CACHE + SELECT + awdp.stake_address, + pi.pool_id, + COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) AS TOTAL_BALANCE, + CASE + WHEN ( COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0) ) <= 0 THEN + COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) + ELSE + COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) - (COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0)) + END AS UTXO, + COALESCE(atrew.REWARDS, 0) AS REWARDS, + COALESCE(atw.WITHDRAWALS, 0) AS WITHDRAWALS, + CASE + WHEN ( COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0) ) <= 0 THEN 0 + ELSE COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0) + END AS REWARDS_AVAILABLE + from accounts_with_delegated_pools awdp + INNER JOIN pool_ids pi ON pi.stake_address_id = awdp.stake_address_id + LEFT JOIN account_active_stake aas ON aas.stake_address_id = awdp.stake_address_id + LEFT JOIN account_total_rewards atrew ON atrew.stake_address_id = awdp.stake_address_id + LEFT JOIN account_total_withdrawals atw ON atw.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_input adi ON adi.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_output ado ON ado.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_rewards adr ON adr.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_withdrawals adw ON adw.stake_address_id = awdp.stake_address_id + ON CONFLICT (STAKE_ADDRESS) DO + UPDATE + SET POOL_ID = EXCLUDED.POOL_ID, + TOTAL_BALANCE = EXCLUDED.TOTAL_BALANCE, + UTXO = EXCLUDED.UTXO, + REWARDS = EXCLUDED.REWARDS, + WITHDRAWALS = EXCLUDED.WITHDRAWALS, + REWARDS_AVAILABLE = EXCLUDED.REWARDS_AVAILABLE + ; + + INSERT INTO GREST.CONTROL_TABLE (key, last_value) + VALUES ( + 'stake_distribution_lbh', + _last_accounted_block_height + ) ON CONFLICT (key) DO + UPDATE + SET last_value = _last_accounted_block_height; + +END; +$$; + +-- HELPER FUNCTION: GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK +-- Determines whether or not the stake distribution cache should be updated +-- based on the time rule (max once in 60 mins), and ensures previous run completed. + +CREATE OR REPLACE FUNCTION GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK () RETURNS VOID LANGUAGE PLPGSQL AS $$ + DECLARE + _last_update_block_height bigint DEFAULT NULL; + _current_block_height bigint DEFAULT NULL; + _last_update_block_diff bigint DEFAULT NULL; + BEGIN IF ( + -- If checking query with the same name there will be 2 results + SELECT COUNT(pid) > 1 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK(%' + AND datname = ( + SELECT current_database() + ) + ) THEN + RAISE EXCEPTION 'Previous query still running but should have completed! Exiting...'; + ELSIF ( + -- If checking query with a different name there will be 1 result + SELECT COUNT(pid) > 0 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%GREST.UPDATE_NEWLY_REGISTERED_ACCOUNTS_STAKE_DISTRIBUTION_CACHE(%' + AND datname = ( + SELECT current_database() + ) + ) THEN + RAISE EXCEPTION 'New accounts query running! Exiting...'; + ELSIF ( + SELECT count(last_value) = 0 FROM GREST.CONTROL_TABLE WHERE key = 'last_active_stake_validated_epoch' + ) OR ( + SELECT ((SELECT MAX(NO) FROM EPOCH) - COALESCE((last_value::integer - 2)::integer, 0 )) > 3 + FROM GREST.CONTROL_TABLE + WHERE key = 'last_active_stake_validated_epoch' + ) THEN + RAISE EXCEPTION 'Active Stake cache too far, skipping...'; + END IF; + + -- QUERY START -- + SELECT COALESCE( + ( + SELECT last_value::bigint + FROM GREST.control_table + WHERE key = 'stake_distribution_lbh' + ), 0 + ) INTO _last_update_block_height; + + SELECT MAX(block_no) + FROM PUBLIC.BLOCK + WHERE BLOCK_NO IS NOT NULL INTO _current_block_height; + + SELECT ( + _current_block_height - _last_update_block_height + ) INTO _last_update_block_diff; + -- Do nothing until there is a 180 blocks difference in height - 60 minutes theoretical time + -- 185 in check because last block height considered is 5 blocks behind tip + + Raise NOTICE 'Last stake distribution update was % blocks ago...', + _last_update_block_diff; + IF (_last_update_block_diff >= 180 + OR _last_update_block_diff < 0 -- Special case for db-sync restart rollback to epoch start + ) THEN + RAISE NOTICE 'Re-running...'; + CALL GREST.UPDATE_STAKE_DISTRIBUTION_CACHE (); + ELSE + RAISE NOTICE 'Minimum block height difference(180) for update not reached, skipping...'; + END IF; + + RETURN; + END; +$$; + +DROP INDEX IF EXISTS GREST.idx_pool_id; +CREATE INDEX idx_pool_id ON GREST.STAKE_DISTRIBUTION_CACHE (POOL_ID); +-- Populated by first crontab execution diff --git a/files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql b/files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql new file mode 100644 index 00000000..3bbd482a --- /dev/null +++ b/files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql @@ -0,0 +1,92 @@ +CREATE OR REPLACE PROCEDURE GREST.UPDATE_NEWLY_REGISTERED_ACCOUNTS_STAKE_DISTRIBUTION_CACHE() LANGUAGE PLPGSQL AS $$ +BEGIN + IF ( + -- If checking query with a different name there will be 1 result + SELECT COUNT(pid) > 1 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%GREST.UPDATE_NEWLY_REGISTERED_ACCOUNTS_STAKE_DISTRIBUTION_CACHE(%' + AND query NOT ILIKE '%pg_stat_activity%' + AND datname = ( + SELECT current_database() + ) + ) THEN + RAISE EXCEPTION 'New accounts query already running! Exiting...'; + ELSIF ( + -- If checking query with a different name there will be 1 result + SELECT COUNT(pid) > 0 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK(%' + AND datname = ( + SELECT current_database() + ) + ) THEN + RAISE NOTICE 'Stake distribution query running! Killing it and running new accounts update...'; + CALL grest.kill_queries_partial_match('GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK('); + END IF; + + WITH + newly_registered_accounts AS ( + SELECT DISTINCT ON (STAKE_ADDRESS.ID) + stake_address.id as stake_address_id, + stake_address.view as stake_address, + pool_hash_id + FROM STAKE_ADDRESS + INNER JOIN DELEGATION ON DELEGATION.ADDR_ID = STAKE_ADDRESS.ID + WHERE + NOT EXISTS ( + SELECT TRUE FROM DELEGATION D + WHERE D.ADDR_ID = DELEGATION.ADDR_ID AND D.ID > DELEGATION.ID + ) + AND NOT EXISTS ( + SELECT TRUE FROM STAKE_DEREGISTRATION + WHERE STAKE_DEREGISTRATION.ADDR_ID = DELEGATION.ADDR_ID + AND STAKE_DEREGISTRATION.TX_ID > DELEGATION.TX_ID + ) + AND NOT EXISTS ( + SELECT TRUE FROM EPOCH_STAKE + WHERE EPOCH_STAKE.EPOCH_NO = ( + SELECT last_value::integer FROM GREST.CONTROL_TABLE + WHERE key = 'last_active_stake_validated_epoch' + ) + AND EPOCH_STAKE.ADDR_ID = STAKE_ADDRESS.ID + ) + ) + -- INSERT QUERY START + INSERT INTO GREST.STAKE_DISTRIBUTION_CACHE + SELECT + nra.stake_address, + ai.delegated_pool as pool_id, + ai.total_balance::lovelace, + ai.utxo::lovelace, + ai.rewards::lovelace, + ai.withdrawals::lovelace, + ai.rewards_available::lovelace + FROM newly_registered_accounts nra, + LATERAL grest.account_info(array[nra.stake_address]) ai + ON CONFLICT (STAKE_ADDRESS) DO + UPDATE + SET + POOL_ID = EXCLUDED.POOL_ID, + TOTAL_BALANCE = EXCLUDED.total_balance, + UTXO = EXCLUDED.utxo, + REWARDS = EXCLUDED.rewards, + WITHDRAWALS = EXCLUDED.withdrawals, + REWARDS_AVAILABLE = EXCLUDED.rewards_available; + + -- Clean up de-registered accounts + DELETE FROM grest.stake_distribution_cache + WHERE stake_address IN ( + SELECT DISTINCT ON (SA.id) + SA.view + FROM STAKE_ADDRESS SA + INNER JOIN STAKE_DEREGISTRATION SD ON SA.ID = SD.ADDR_ID + WHERE NOT EXISTS ( + SELECT TRUE FROM STAKE_REGISTRATION SR + WHERE SR.ADDR_ID = SD.ADDR_ID + AND SR.TX_ID > SD.TX_ID + ) + ); +END; +$$; diff --git a/files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql b/files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql new file mode 100644 index 00000000..bfdecf95 --- /dev/null +++ b/files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql @@ -0,0 +1,400 @@ +/* Keeps track of stake snapshots taken at the end of epochs n - 1 and n - 2 */ +CREATE TABLE IF NOT EXISTS GREST.stake_snapshot_cache ( + addr_id integer, + pool_id integer, + amount numeric, + epoch_no bigint, + PRIMARY KEY (addr_id, epoch_no) +); + +CREATE INDEX IF NOT EXISTS _idx_pool_id ON grest.stake_snapshot_cache (pool_id); +CREATE INDEX IF NOT EXISTS _idx_addr_id ON grest.stake_snapshot_cache (addr_id); + +CREATE OR REPLACE PROCEDURE GREST.CAPTURE_LAST_EPOCH_SNAPSHOT () +LANGUAGE PLPGSQL +AS $$ +DECLARE + _previous_epoch_no bigint; + _lower_bound_account_tx_id bigint; + _upper_bound_account_tx_id bigint; + _newly_registered_account_ids bigint[]; +BEGIN + IF ( + -- If checking query with the same name there will be 2 results + SELECT COUNT(pid) > 1 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%GREST.CAPTURE_LAST_EPOCH_SNAPSHOT(%' + AND datname = ( + SELECT current_database() + ) + ) THEN + RAISE EXCEPTION 'Previous query still running but should have completed! Exiting...'; + END IF; + + SELECT MAX(NO) - 1 INTO _previous_epoch_no FROM PUBLIC.EPOCH; + + IF NOT EXISTS ( + SELECT i_last_tx_id from grest.epoch_info_cache + WHERE epoch_no = _previous_epoch_no + AND i_last_tx_id IS NOT NULL + ) THEN + RAISE NOTICE 'Epoch % info cache not ready, exiting.', _previous_epoch_no; + RETURN; + END IF; + + IF EXISTS ( + SELECT FROM grest.stake_snapshot_cache + WHERE epoch_no = _previous_epoch_no + ) THEN + RETURN; + END IF; + + -- Set-up interval limits for previous epoch + SELECT MAX(tx.id) INTO _lower_bound_account_tx_id + FROM PUBLIC.TX + INNER JOIN BLOCK b ON b.id = tx.block_id + WHERE b.epoch_no <= _previous_epoch_no - 2 + AND b.block_no IS NOT NULL + AND b.tx_count != 0; + + SELECT MAX(tx.id) INTO _upper_bound_account_tx_id + FROM PUBLIC.TX + INNER JOIN BLOCK b ON b.id = tx.block_id + WHERE b.epoch_no <= _previous_epoch_no + AND b.block_no IS NOT NULL + AND b.tx_count != 0; + + /* Temporary table to figure out valid delegations ending up in active stake in case of pool retires */ + DROP TABLE IF EXISTS minimum_pool_delegation_tx_ids; + CREATE TEMP TABLE minimum_pool_delegation_tx_ids ( + pool_hash_id integer PRIMARY KEY, + latest_registered_tx_id integer, + latest_registered_tx_cert_index integer + ); + + DROP TABLE IF EXISTS latest_accounts_delegation_txs; + CREATE TEMP TABLE latest_accounts_delegation_txs ( + addr_id integer PRIMARY KEY, + tx_id integer, + cert_index integer, + pool_hash_id integer + ); + + DROP TABLE IF EXISTS rewards_subset; + CREATE TEMP TABLE rewards_subset ( + stake_address_id bigint, + type rewardtype, + spendable_epoch bigint, + amount lovelace + ); + + INSERT INTO rewards_subset + SELECT addr_id, type, spendable_epoch, amount + FROM reward + WHERE spendable_epoch BETWEEN _previous_epoch_no - 1 AND _previous_epoch_no + 1; + +/* Registered and delegated accounts to be captured (have epoch_stake entries for baseline) */ + WITH + latest_non_cancelled_pool_retire as ( + SELECT DISTINCT ON (pr.hash_id) + pr.hash_id, + pr.retiring_epoch + FROM pool_retire pr + WHERE + pr.announced_tx_id <= _upper_bound_account_tx_id + AND pr.retiring_epoch <= _previous_epoch_no + AND NOT EXISTS ( + SELECT TRUE + FROM pool_update pu + WHERE pu.hash_id = pr.hash_id + AND ( + pu.registered_tx_id > pr.announced_tx_id + OR ( + pu.registered_tx_id = pr.announced_tx_id + AND pu.cert_index > pr.cert_index + ) + ) + AND pu.registered_tx_id <= _upper_bound_account_tx_id + AND pu.registered_tx_id <= ( + SELECT i_last_tx_id + FROM grest.epoch_info_cache eic + WHERE eic.epoch_no = pr.retiring_epoch - 1 + ) + ) + AND NOT EXISTS ( + SELECT TRUE + FROM pool_retire sub_pr + WHERE pr.hash_id = sub_pr.hash_id + AND ( + sub_pr.announced_tx_id > pr.announced_tx_id + OR ( + sub_pr.announced_tx_id = pr.announced_tx_id + AND sub_pr.cert_index > pr.cert_index + ) + ) + AND sub_pr.announced_tx_id <= _upper_bound_account_tx_id + AND sub_pr.announced_tx_id <= ( + SELECT i_last_tx_id + FROM grest.epoch_info_cache eic + WHERE eic.epoch_no = pr.retiring_epoch - 1 + ) + ) + ORDER BY + pr.hash_id, pr.retiring_epoch DESC + ) + + INSERT INTO minimum_pool_delegation_tx_ids + SELECT DISTINCT ON (pu.hash_id) + pu.hash_id, + pu.registered_tx_id as min_tx_id, + pu.cert_index + FROM pool_update pu + LEFT JOIN latest_non_cancelled_pool_retire lncpr ON lncpr.hash_id = pu.hash_id + WHERE pu.registered_tx_id <= _upper_bound_account_tx_id + AND + CASE WHEN lncpr.retiring_epoch IS NOT NULL + THEN + pu.registered_tx_id > ( + SELECT i_last_tx_id + FROM grest.epoch_info_cache eic + WHERE eic.epoch_no = lncpr.retiring_epoch - 1 + ) + ELSE TRUE + END + ORDER BY + pu.hash_id, pu.registered_tx_id ASC; + + INSERT INTO latest_accounts_delegation_txs + SELECT distinct on (d.addr_id) + d.addr_id, + d.tx_id, + d.cert_index, + d.pool_hash_id + FROM DELEGATION D + WHERE + d.tx_id <= _upper_bound_account_tx_id + AND NOT EXISTS ( + SELECT TRUE FROM STAKE_DEREGISTRATION + WHERE STAKE_DEREGISTRATION.ADDR_ID = D.ADDR_ID + AND ( + STAKE_DEREGISTRATION.TX_ID > D.TX_ID + OR ( + STAKE_DEREGISTRATION.TX_ID = D.TX_ID + AND STAKE_DEREGISTRATION.CERT_INDEX > D.CERT_INDEX + ) + ) + AND STAKE_DEREGISTRATION.TX_ID <= _upper_bound_account_tx_id + ) + ORDER BY + d.addr_id, d.tx_id DESC; + + CREATE INDEX _idx_pool_hash_id ON latest_accounts_delegation_txs (pool_hash_id); + + + /* Registered and delegated accounts to be captured (have epoch_stake entries for baseline) */ + WITH + accounts_with_delegated_pools AS ( + SELECT DISTINCT ON (ladt.addr_id) + ladt.addr_id as stake_address_id, + ladt.pool_hash_id + FROM latest_accounts_delegation_txs ladt + INNER JOIN minimum_pool_delegation_tx_ids mpdtx ON mpdtx.pool_hash_id = ladt.pool_hash_id + WHERE + ( + ladt.tx_id > mpdtx.latest_registered_tx_id + OR ( + ladt.tx_id = mpdtx.latest_registered_tx_id + AND ladt.cert_index > mpdtx.latest_registered_tx_cert_index + ) + ) + -- Account must be present in epoch_stake table for the previous epoch + AND EXISTS ( + SELECT TRUE FROM epoch_stake es + WHERE es.epoch_no = _previous_epoch_no + AND es.addr_id = ladt.addr_id + ) + ), + account_active_stake AS ( + SELECT awdp.stake_address_id, es.amount + FROM public.epoch_stake es + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = es.addr_id + WHERE epoch_no = _previous_epoch_no + ), + account_delta_tx_ins AS ( + SELECT awdp.stake_address_id, tx_in.tx_out_id AS txoid, tx_in.tx_out_index AS txoidx FROM tx_in + LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE tx_in.tx_in_id > _lower_bound_account_tx_id + AND tx_in.tx_in_id <= _upper_bound_account_tx_id + ), + account_delta_input AS ( + SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + FROM account_delta_tx_ins + LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id + GROUP BY tx_out.stake_address_id + ), + account_delta_output AS ( + SELECT awdp.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + FROM tx_out + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE TX_OUT.TX_ID > _lower_bound_account_tx_id + AND TX_OUT.TX_ID <= _upper_bound_account_tx_id + GROUP BY awdp.stake_address_id + ), + account_delta_rewards AS ( + SELECT awdp.stake_address_id, COALESCE(SUM(rs.amount), 0) AS REWARDS + FROM rewards_subset rs + INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = rs.stake_address_id + WHERE + CASE WHEN rs.type = 'refund' + THEN rs.spendable_epoch IN (_previous_epoch_no - 1, _previous_epoch_no) + ELSE rs.spendable_epoch IN (_previous_epoch_no, _previous_epoch_no + 1) + END + GROUP BY awdp.stake_address_id + ), + account_delta_withdrawals AS ( + SELECT accounts_with_delegated_pools.stake_address_id, COALESCE(SUM(withdrawal.amount), 0) AS withdrawals + FROM withdrawal + INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = withdrawal.addr_id + WHERE withdrawal.tx_id > _lower_bound_account_tx_id + AND withdrawal.tx_id <= _upper_bound_account_tx_id + GROUP BY accounts_with_delegated_pools.stake_address_id + ) + + INSERT INTO GREST.stake_snapshot_cache + SELECT + awdp.stake_address_id as addr_id, + awdp.pool_hash_id, + COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) as AMOUNT, + _previous_epoch_no as epoch_no + from accounts_with_delegated_pools awdp + LEFT JOIN account_active_stake aas ON aas.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_input adi ON adi.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_output ado ON ado.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_rewards adr ON adr.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_withdrawals adw ON adw.stake_address_id = awdp.stake_address_id + ON CONFLICT (addr_id, EPOCH_NO) DO + UPDATE + SET + POOL_ID = EXCLUDED.POOL_ID, + AMOUNT = EXCLUDED.AMOUNT; + + /* Newly registered accounts to be captured (they don't have epoch_stake entries for baseline) */ + SELECT INTO _newly_registered_account_ids ARRAY_AGG(addr_id) + FROM ( + SELECT DISTINCT ladt.addr_id + FROM latest_accounts_delegation_txs ladt + INNER JOIN minimum_pool_delegation_tx_ids mpdtx ON mpdtx.pool_hash_id = ladt.pool_hash_id + WHERE + ( + ladt.tx_id > mpdtx.latest_registered_tx_id + OR ( + ladt.tx_id = mpdtx.latest_registered_tx_id + AND ladt.cert_index > mpdtx.latest_registered_tx_cert_index + ) + ) + -- Account must NOT be present in epoch_stake table for the previous epoch + AND NOT EXISTS ( + SELECT TRUE FROM epoch_stake es + WHERE es.epoch_no = _previous_epoch_no + AND es.addr_id = ladt.addr_id + ) + ) AS tmp; + WITH + account_delta_tx_ins AS ( + SELECT tx_out.stake_address_id, tx_in.tx_out_id AS txoid, tx_in.tx_out_index AS txoidx + FROM tx_in + LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint + WHERE tx_in.tx_in_id <= _upper_bound_account_tx_id + AND tx_out.stake_address_id = ANY(_newly_registered_account_ids) + ), + account_delta_input AS ( + SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + FROM account_delta_tx_ins + LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index + WHERE tx_out.stake_address_id = ANY(_newly_registered_account_ids) + GROUP BY tx_out.stake_address_id + ), + account_delta_output AS ( + SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + FROM tx_out + WHERE TX_OUT.TX_ID <= _upper_bound_account_tx_id + AND tx_out.stake_address_id = ANY(_newly_registered_account_ids) + GROUP BY tx_out.stake_address_id + ), + account_delta_rewards AS ( + SELECT r.addr_id as stake_address_id, COALESCE(SUM(r.amount), 0) AS REWARDS + FROM REWARD r + WHERE r.addr_id = ANY(_newly_registered_account_ids) + AND + CASE WHEN r.type = 'refund' + THEN r.spendable_epoch <= _previous_epoch_no + ELSE r.spendable_epoch <= _previous_epoch_no + 1 + END + GROUP BY r.addr_id + ), + account_delta_withdrawals AS ( + SELECT withdrawal.addr_id as stake_address_id, COALESCE(SUM(withdrawal.amount), 0) AS withdrawals + FROM withdrawal + WHERE withdrawal.tx_id <= _upper_bound_account_tx_id + AND withdrawal.addr_id = ANY(_newly_registered_account_ids) + GROUP BY withdrawal.addr_id + ) + + INSERT INTO GREST.stake_snapshot_cache + SELECT + ladt.addr_id, + ladt.pool_hash_id, + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) as amount, + _previous_epoch_no as epoch_no + FROM latest_accounts_delegation_txs ladt + LEFT JOIN account_delta_input adi ON adi.stake_address_id = ladt.addr_id + LEFT JOIN account_delta_output ado ON ado.stake_address_id = ladt.addr_id + LEFT JOIN account_delta_rewards adr ON adr.stake_address_id = ladt.addr_id + LEFT JOIN account_delta_withdrawals adw ON adw.stake_address_id = ladt.addr_id + WHERE + ladt.addr_id = ANY(_newly_registered_account_ids) + ON CONFLICT (addr_id, epoch_no) DO + UPDATE + SET + pool_id = EXCLUDED.pool_id, + amount = EXCLUDED.amount; + + INSERT INTO GREST.CONTROL_TABLE (key, last_value) + VALUES ( + 'last_stake_snapshot_epoch', + _previous_epoch_no + ) ON CONFLICT (key) + DO UPDATE + SET last_value = _previous_epoch_no; + + INSERT INTO grest.epoch_active_stake_cache + SELECT + _previous_epoch_no + 2, + SUM(amount) + FROM grest.stake_snapshot_cache + WHERE epoch_no = _previous_epoch_no + ON CONFLICT (epoch_no) DO UPDATE + SET amount = excluded.amount + WHERE epoch_active_stake_cache.amount IS DISTINCT FROM excluded.amount; + + INSERT INTO grest.pool_active_stake_cache + SELECT + ph.view, + _previous_epoch_no + 2, + SUM(ssc.amount) + FROM grest.stake_snapshot_cache ssc + INNER JOIN pool_hash ph ON ph.id = ssc.pool_id + WHERE epoch_no = _previous_epoch_no + GROUP BY + ssc.pool_id, ph.view + ON CONFLICT (pool_id, epoch_no) DO UPDATE + SET amount = excluded.amount + WHERE pool_active_stake_cache.amount IS DISTINCT FROM excluded.amount; + + DELETE FROM grest.stake_snapshot_cache + WHERE epoch_no <= _previous_epoch_no - 2; +END; +$$; diff --git a/files/grest/rpc/account/account_addresses.sql b/files/grest/rpc/account/account_addresses.sql new file mode 100644 index 00000000..070730b8 --- /dev/null +++ b/files/grest/rpc/account/account_addresses.sql @@ -0,0 +1,40 @@ +CREATE FUNCTION grest.account_addresses (_stake_addresses text[]) + RETURNS TABLE ( + stake_address varchar, + addresses json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + sa_id_list integer[]; +BEGIN + SELECT INTO sa_id_list + ARRAY_AGG(STAKE_ADDRESS.ID) + FROM + STAKE_ADDRESS + WHERE + STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + + RETURN QUERY + WITH txo_addr AS ( + SELECT address, stake_address_id FROM + (SELECT + DISTINCT ON (address) address, stake_address_id, id + FROM + TX_OUT + WHERE stake_address_id = ANY(sa_id_list) ORDER BY address, id) x + ORDER BY id + ) + SELECT + sa.view as stake_address, + JSON_AGG(txo_addr.address) as addresses + FROM + txo_addr + INNER JOIN STAKE_ADDRESS sa ON sa.id = txo_addr.stake_address_id + GROUP BY + sa.id; +END; +$$; + +COMMENT ON FUNCTION grest.account_addresses IS 'Get all addresses associated with given accounts'; + diff --git a/files/grest/rpc/account/account_assets.sql b/files/grest/rpc/account/account_assets.sql new file mode 100644 index 00000000..b07acb56 --- /dev/null +++ b/files/grest/rpc/account/account_assets.sql @@ -0,0 +1,63 @@ +CREATE FUNCTION grest.account_assets (_stake_addresses text[]) + RETURNS TABLE ( + stake_address varchar, + asset_list json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + sa_id_list integer[]; +BEGIN + SELECT INTO sa_id_list + ARRAY_AGG(STAKE_ADDRESS.ID) + FROM + STAKE_ADDRESS + WHERE + STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + + RETURN QUERY + WITH _all_assets AS ( + SELECT + sa.view, + ma.policy, + ma.name, + ma.fingerprint, + SUM(mtx.quantity) as quantity + FROM + MA_TX_OUT MTX + INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident + INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID + INNER JOIN STAKE_ADDRESS sa ON sa.id = TXO.stake_address_id + LEFT JOIN TX_IN on TXO.TX_ID = TX_IN.TX_OUT_ID + AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint + WHERE + sa.id = ANY(sa_id_list) + AND TX_IN.TX_IN_ID IS NULL + GROUP BY + sa.view, MA.policy, MA.name, MA.fingerprint + ) + + SELECT + assets_grouped.view as stake_address, + assets_grouped.assets + FROM ( + SELECT + aa.view, + JSON_AGG( + JSON_BUILD_OBJECT( + 'policy_id', ENCODE(aa.policy, 'hex'), + 'asset_name', ENCODE(aa.name, 'hex'), + 'fingerprint', aa.fingerprint, + 'quantity', aa.quantity::text + ) + ) as assets + FROM + _all_assets aa + GROUP BY + aa.view + ) assets_grouped; +END; +$$; + +COMMENT ON FUNCTION grest.account_assets IS 'Get the native asset balance of given accounts'; + diff --git a/files/grest/rpc/account/account_history.sql b/files/grest/rpc/account/account_history.sql new file mode 100644 index 00000000..79f2f184 --- /dev/null +++ b/files/grest/rpc/account/account_history.sql @@ -0,0 +1,63 @@ +CREATE FUNCTION grest.account_history (_stake_addresses text[], _epoch_no integer DEFAULT NULL) + RETURNS TABLE ( + stake_address varchar, + history json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + sa_id_list integer[]; +BEGIN + SELECT INTO sa_id_list + ARRAY_AGG(STAKE_ADDRESS.ID) + FROM + STAKE_ADDRESS + WHERE + STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + + IF _epoch_no IS NOT NULL THEN + RETURN QUERY + SELECT + sa.view as stake_address, + JSON_AGG( + JSON_BUILD_OBJECT( + 'pool_id', ph.view, + 'epoch_no', es.epoch_no::bigint, + 'active_stake', es.amount::text + ) + ) + FROM + EPOCH_STAKE es + LEFT JOIN stake_address sa ON sa.id = es.addr_id + LEFT JOIN pool_hash ph ON ph.id = es.pool_id + WHERE + es.epoch_no = _epoch_no + AND + sa.id = ANY(sa_id_list) + GROUP BY + sa.view; + ELSE + RETURN QUERY + SELECT + sa.view as stake_address, + JSON_AGG( + JSON_BUILD_OBJECT( + 'pool_id', ph.view, + 'epoch_no', es.epoch_no::bigint, + 'active_stake', es.amount::text + ) + ) + FROM + EPOCH_STAKE es + LEFT JOIN stake_address sa ON sa.id = es.addr_id + LEFT JOIN pool_hash ph ON ph.id = es.pool_id + WHERE + sa.id = ANY(sa_id_list) + GROUP BY + sa.view; + END IF; +END; +$$; + +COMMENT ON FUNCTION grest.account_history IS 'Get the active stake history of given accounts'; + diff --git a/files/grest/rpc/account/account_info.sql b/files/grest/rpc/account/account_info.sql new file mode 100644 index 00000000..4a64701f --- /dev/null +++ b/files/grest/rpc/account/account_info.sql @@ -0,0 +1,186 @@ +CREATE FUNCTION grest.account_info (_stake_addresses text[]) + RETURNS TABLE ( + stake_address varchar, + STATUS text, + DELEGATED_POOL varchar, + TOTAL_BALANCE text, + UTXO text, + REWARDS text, + WITHDRAWALS text, + REWARDS_AVAILABLE text, + RESERVES text, + TREASURY text) + LANGUAGE PLPGSQL + AS $$ +DECLARE + sa_id_list integer[] DEFAULT NULL; +BEGIN + SELECT INTO sa_id_list + array_agg(ID) + FROM + STAKE_ADDRESS + WHERE + STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + + RETURN QUERY + WITH latest_withdrawal_txs AS ( + SELECT DISTINCT ON (addr_id) + addr_id, + tx_id + FROM WITHDRAWAL + WHERE ADDR_ID = ANY(sa_id_list) + ORDER BY addr_id, TX_ID DESC + ), + latest_withdrawal_epochs AS ( + SELECT + lwt.addr_id, + b.epoch_no + FROM BLOCK b + INNER JOIN TX ON TX.BLOCK_ID = b.ID + INNER JOIN latest_withdrawal_txs lwt ON tx.id = lwt.tx_id + ) + + SELECT + STATUS_T.view as stake_address, + CASE WHEN STATUS_T.REGISTERED = TRUE THEN + 'registered' + ELSE + 'not registered' + END AS STATUS, + POOL_T.DELEGATED_POOL, + CASE WHEN (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)) < 0 THEN + (COALESCE(UTXO_T.UTXO, 0) + COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0) + COALESCE(RESERVES_T.RESERVES, 0) + COALESCE(TREASURY_T.TREASURY, 0) - (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)))::text + ELSE + (COALESCE(UTXO_T.UTXO, 0) + COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0) + COALESCE(RESERVES_T.RESERVES, 0) + COALESCE(TREASURY_T.TREASURY, 0))::text + END AS TOTAL_BALANCE, + COALESCE(UTXO_T.UTXO, 0)::text AS UTXO, + COALESCE(REWARDS_T.REWARDS, 0)::text AS REWARDS, + COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)::text AS WITHDRAWALS, + CASE WHEN (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)) <= 0 THEN + '0' + ELSE + (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0))::text + END AS REWARDS_AVAILABLE, + COALESCE(RESERVES_T.RESERVES, 0)::text AS RESERVES, + COALESCE(TREASURY_T.TREASURY, 0)::text AS TREASURY + FROM + ( + SELECT + sas.id, + sas.view, + EXISTS ( + SELECT TRUE FROM STAKE_REGISTRATION + WHERE + STAKE_REGISTRATION.ADDR_ID = sas.id + AND NOT EXISTS ( + SELECT TRUE + FROM STAKE_DEREGISTRATION + WHERE + STAKE_DEREGISTRATION.ADDR_ID = STAKE_REGISTRATION.ADDR_ID + AND STAKE_DEREGISTRATION.TX_ID > STAKE_REGISTRATION.TX_ID + ) + ) AS REGISTERED + FROM public.stake_address sas + WHERE sas.id = ANY(sa_id_list) + ) STATUS_T + LEFT JOIN ( + SELECT + delegation.addr_id, + POOL_HASH.VIEW AS DELEGATED_POOL + FROM + DELEGATION + INNER JOIN POOL_HASH ON POOL_HASH.ID = DELEGATION.POOL_HASH_ID + WHERE + DELEGATION.ADDR_ID = ANY(sa_id_list) + AND NOT EXISTS ( + SELECT + TRUE + FROM + DELEGATION D + WHERE + D.ADDR_ID = DELEGATION.ADDR_ID + AND D.ID > DELEGATION.ID) + AND NOT EXISTS ( + SELECT + TRUE + FROM + STAKE_DEREGISTRATION + WHERE + STAKE_DEREGISTRATION.ADDR_ID = DELEGATION.ADDR_ID + AND STAKE_DEREGISTRATION.TX_ID > DELEGATION.TX_ID) + ) POOL_T ON POOL_T.addr_id = status_t.id + LEFT JOIN ( + SELECT + TX_OUT.STAKE_ADDRESS_ID, + COALESCE(SUM(VALUE), 0) AS UTXO + FROM + TX_OUT + LEFT JOIN TX_IN ON TX_OUT.TX_ID = TX_IN.TX_OUT_ID + AND TX_OUT.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint + WHERE + TX_OUT.STAKE_ADDRESS_ID = ANY(sa_id_list) + AND TX_IN.TX_IN_ID IS NULL + GROUP BY + tx_out.stake_address_id + ) UTXO_T ON UTXO_T.stake_address_id = status_t.id + LEFT JOIN ( + SELECT + REWARD.ADDR_ID, + COALESCE(SUM(REWARD.AMOUNT), 0) AS REWARDS + FROM + REWARD + WHERE + REWARD.ADDR_ID = ANY(sa_id_list) + AND REWARD.SPENDABLE_EPOCH <= ( + SELECT MAX(NO) + FROM EPOCH + ) + GROUP BY + REWARD.ADDR_ID + ) REWARDS_T ON REWARDS_T.addr_id = status_t.id + LEFT JOIN ( + SELECT + WITHDRAWAL.ADDR_ID, + COALESCE(SUM(WITHDRAWAL.AMOUNT), 0) AS WITHDRAWALS + FROM + WITHDRAWAL + WHERE + WITHDRAWAL.ADDR_ID = ANY(sa_id_list) + GROUP BY + WITHDRAWAL.ADDR_ID + ) WITHDRAWALS_T ON WITHDRAWALS_T.addr_id = status_t.id + LEFT JOIN ( + SELECT + RESERVE.ADDR_ID, + COALESCE(SUM(RESERVE.AMOUNT), 0) AS RESERVES + FROM + RESERVE + INNER JOIN TX ON TX.ID = RESERVE.TX_ID + INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID + INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = reserve.addr_id + WHERE + RESERVE.ADDR_ID = ANY(sa_id_list) + AND BLOCK.EPOCH_NO >= lwe.epoch_no + GROUP BY + RESERVE.ADDR_ID + ) RESERVES_T ON RESERVES_T.addr_id = status_t.id + LEFT JOIN ( + SELECT + TREASURY.ADDR_ID, + COALESCE(SUM(TREASURY.AMOUNT), 0) AS TREASURY + FROM + TREASURY + INNER JOIN TX ON TX.ID = TREASURY.TX_ID + INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID + INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = TREASURY.addr_id + WHERE + TREASURY.ADDR_ID = ANY(sa_id_list) + AND BLOCK.EPOCH_NO >= lwe.epoch_no + GROUP BY + TREASURY.ADDR_ID + ) TREASURY_T ON TREASURY_T.addr_id = status_t.id; +END; +$$; + +COMMENT ON FUNCTION grest.account_info IS 'Get the account info for given stake addresses'; + diff --git a/files/grest/rpc/account/account_info_cached.sql b/files/grest/rpc/account/account_info_cached.sql new file mode 100644 index 00000000..6af91e4b --- /dev/null +++ b/files/grest/rpc/account/account_info_cached.sql @@ -0,0 +1,113 @@ +CREATE OR REPLACE FUNCTION grest.account_info_cached (_stake_addresses text[]) + RETURNS TABLE ( + stake_address varchar, + STATUS text, + DELEGATED_POOL varchar, + TOTAL_BALANCE text, + UTXO text, + REWARDS text, + WITHDRAWALS text, + REWARDS_AVAILABLE text, + RESERVES text, + TREASURY text) + LANGUAGE PLPGSQL + AS $$ +DECLARE + sa_id_list integer[] DEFAULT NULL; +BEGIN + SELECT INTO sa_id_list + array_agg(ID) + FROM + STAKE_ADDRESS + WHERE + STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + + RETURN QUERY + WITH latest_withdrawal_txs AS ( + SELECT DISTINCT ON (addr_id) + addr_id, + tx_id + FROM WITHDRAWAL + WHERE ADDR_ID = ANY(sa_id_list) + ORDER BY addr_id, TX_ID DESC + ), + latest_withdrawal_epochs AS ( + SELECT + lwt.addr_id, + b.epoch_no + FROM BLOCK b + INNER JOIN TX ON TX.BLOCK_ID = b.ID + INNER JOIN latest_withdrawal_txs lwt ON tx.id = lwt.tx_id + ) + + SELECT + sdc.stake_address, + CASE WHEN STATUS_T.REGISTERED = TRUE THEN + 'registered' + ELSE + 'not registered' + END AS status, + sdc.pool_id as pool_id, + sdc.total_balance::text, + sdc.utxo::text, + sdc.rewards::text, + sdc.withdrawals::text, + sdc.rewards_available::text, + COALESCE(RESERVES_T.RESERVES, 0)::text AS RESERVES, + COALESCE(TREASURY_T.TREASURY, 0)::text AS TREASURY + FROM + grest.stake_distribution_cache sdc + LEFT JOIN ( + SELECT + sas.id, + sas.view, + EXISTS ( + SELECT TRUE FROM STAKE_REGISTRATION + WHERE + STAKE_REGISTRATION.ADDR_ID = sas.id + AND NOT EXISTS ( + SELECT TRUE + FROM STAKE_DEREGISTRATION + WHERE + STAKE_DEREGISTRATION.ADDR_ID = STAKE_REGISTRATION.ADDR_ID + AND STAKE_DEREGISTRATION.TX_ID > STAKE_REGISTRATION.TX_ID + ) + ) AS REGISTERED + FROM public.stake_address sas + WHERE sas.id = ANY(sa_id_list) + ) STATUS_T ON sdc.stake_address = STATUS_T.view + LEFT JOIN ( + SELECT + RESERVE.ADDR_ID, + COALESCE(SUM(RESERVE.AMOUNT), 0) AS RESERVES + FROM + RESERVE + INNER JOIN TX ON TX.ID = RESERVE.TX_ID + INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID + INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = reserve.addr_id + WHERE + RESERVE.ADDR_ID = ANY(sa_id_list) + AND BLOCK.EPOCH_NO >= lwe.epoch_no + GROUP BY + RESERVE.ADDR_ID + ) RESERVES_T ON RESERVES_T.addr_id = status_t.id + LEFT JOIN ( + SELECT + TREASURY.ADDR_ID, + COALESCE(SUM(TREASURY.AMOUNT), 0) AS TREASURY + FROM + TREASURY + INNER JOIN TX ON TX.ID = TREASURY.TX_ID + INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID + INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = TREASURY.addr_id + WHERE + TREASURY.ADDR_ID = ANY(sa_id_list) + AND BLOCK.EPOCH_NO >= lwe.epoch_no + GROUP BY + TREASURY.ADDR_ID + ) TREASURY_T ON TREASURY_T.addr_id = status_t.id + WHERE sdc.stake_address = ANY(_stake_addresses); +END; +$$; + +COMMENT ON FUNCTION grest.account_info IS 'Get the cached account information for given stake addresses'; diff --git a/files/grest/rpc/account/account_rewards.sql b/files/grest/rpc/account/account_rewards.sql new file mode 100644 index 00000000..925c6434 --- /dev/null +++ b/files/grest/rpc/account/account_rewards.sql @@ -0,0 +1,66 @@ +CREATE FUNCTION grest.account_rewards (_stake_addresses text[], _epoch_no numeric DEFAULT NULL) + RETURNS TABLE ( + stake_address varchar, + rewards json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + sa_id_list integer[]; +BEGIN + SELECT INTO sa_id_list + ARRAY_AGG(STAKE_ADDRESS.ID) + FROM + STAKE_ADDRESS + WHERE + STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + + IF _epoch_no IS NULL THEN + RETURN QUERY + SELECT + sa.view, + JSON_AGG( + JSON_BUILD_OBJECT( + 'earned_epoch', r.earned_epoch, + 'spendable_epoch', r.spendable_epoch, + 'amount', r.amount::text, + 'type', r.type, + 'pool_id', ph.view + ) + ) as rewards + FROM + reward AS r + LEFT JOIN pool_hash AS ph ON r.pool_id = ph.id + INNER JOIN stake_address sa ON sa.id = r.addr_id + WHERE + r.addr_id = ANY(sa_id_list) + GROUP BY + sa.id; + ELSE + RETURN QUERY + SELECT + sa.view, + JSON_AGG( + JSON_BUILD_OBJECT( + 'earned_epoch', r.earned_epoch, + 'spendable_epoch', r.spendable_epoch, + 'amount', r.amount::text, + 'type', r.type, + 'pool_id', ph.view + ) + ) as rewards + FROM + reward r + LEFT JOIN pool_hash ph ON r.pool_id = ph.id + INNER JOIN stake_address sa ON sa.id = r.addr_id + WHERE + r.addr_id = ANY(sa_id_list) + AND r.earned_epoch = _epoch_no + GROUP BY + sa.id; + END IF; +END; +$$; + +COMMENT ON FUNCTION grest.account_rewards IS 'Get the full rewards history (including MIR) for given stake addresses, or certain epoch if specified'; + diff --git a/files/grest/rpc/account/account_updates.sql b/files/grest/rpc/account/account_updates.sql new file mode 100644 index 00000000..2e5ce89d --- /dev/null +++ b/files/grest/rpc/account/account_updates.sql @@ -0,0 +1,79 @@ +CREATE FUNCTION grest.account_updates (_stake_addresses text[]) + RETURNS TABLE ( + stake_address varchar, + updates json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + sa_id_list integer[] DEFAULT NULL; +BEGIN + SELECT INTO sa_id_list + ARRAY_AGG(STAKE_ADDRESS.ID) + FROM + STAKE_ADDRESS + WHERE + STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + + RETURN QUERY + SELECT + SA.view as stake_address, + JSON_AGG( + JSON_BUILD_OBJECT( + 'action_type', ACTIONS.action_type, + 'tx_hash', ENCODE(TX.HASH, 'hex'), + 'epoch_no', b.epoch_no, + 'epoch_slot', b.epoch_slot_no, + 'absolute_slot', b.slot_no, + 'block_time', EXTRACT(epoch from b.time)::integer + ) + ) + FROM ( + ( + SELECT + 'registration' AS action_type, + tx_id, + addr_id + FROM + STAKE_REGISTRATION + WHERE + addr_id = ANY(sa_id_list) + ) UNION ( + SELECT + 'deregistration' AS action_type, + tx_id, + addr_id + FROM + STAKE_DEREGISTRATION + WHERE + addr_id = ANY(sa_id_list) + ) UNION ( + SELECT + 'delegation' AS action_type, + tx_id, + addr_id + FROM + DELEGATION + WHERE + addr_id = ANY(sa_id_list) + ) UNION ( + SELECT + 'withdrawal' AS action_type, + tx_id, + addr_id + FROM + WITHDRAWAL + WHERE + addr_id = ANY(sa_id_list) + ) + ) ACTIONS + INNER JOIN TX ON TX.ID = ACTIONS.TX_ID + INNER JOIN STAKE_ADDRESS sa ON sa.id = actions.addr_id + INNER JOIN BLOCK b ON b.id = tx.block_id + GROUP BY + sa.id; +END; +$$; + +COMMENT ON FUNCTION grest.account_updates IS 'Get updates (registration, deregistration, delegation and withdrawals) for given stake addresses'; + diff --git a/files/grest/rpc/address/address_assets.sql b/files/grest/rpc/address/address_assets.sql new file mode 100644 index 00000000..59a4efb7 --- /dev/null +++ b/files/grest/rpc/address/address_assets.sql @@ -0,0 +1,54 @@ +CREATE FUNCTION grest.address_assets (_addresses text[]) + RETURNS TABLE ( + address varchar, + asset_list json + ) + LANGUAGE PLPGSQL + AS $$ +BEGIN + RETURN QUERY + + WITH _all_assets AS ( + SELECT + txo.address, + ma.policy, + ma.name, + ma.fingerprint, + SUM(mtx.quantity) as quantity + FROM + MA_TX_OUT MTX + INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident + INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID + LEFT JOIN TX_IN ON TXO.TX_ID = TX_IN.TX_OUT_ID + AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint + WHERE + TXO.address = ANY(_addresses) + AND TX_IN.TX_IN_ID IS NULL + GROUP BY + TXO.address, MA.policy, MA.name, ma.fingerprint + ) + + SELECT + assets_grouped.address, + assets_grouped.asset_list + FROM ( + SELECT + aa.address, + JSON_AGG( + JSON_BUILD_OBJECT( + 'policy_id', ENCODE(aa.policy, 'hex'), + 'asset_name', ENCODE(aa.name, 'hex'), + 'fingerprint', aa.fingerprint, + 'quantity', aa.quantity::text + ) + ) as asset_list + FROM + _all_assets aa + GROUP BY + aa.address + ) assets_grouped; +END; +$$; + +COMMENT ON FUNCTION grest.address_assets IS 'Get the list of all the assets (policy, name and quantity) for given addresses'; + diff --git a/files/grest/rpc/address/address_info.sql b/files/grest/rpc/address/address_info.sql new file mode 100644 index 00000000..ef6a6cdb --- /dev/null +++ b/files/grest/rpc/address/address_info.sql @@ -0,0 +1,121 @@ +CREATE FUNCTION grest.address_info (_addresses text[]) + RETURNS TABLE ( + address varchar, + balance text, + stake_address character varying, + script_address boolean, + utxo_set json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + known_addresses varchar[]; +BEGIN + + CREATE TEMPORARY TABLE _known_addresses AS + SELECT + DISTINCT ON (tx_out.address) tx_out.address, + sa.view as stake_address, + COALESCE(tx_out.address_has_script, 'false') as script_address + FROM + tx_out + LEFT JOIN stake_address SA on sa.id = tx_out.stake_address_id + WHERE + tx_out.address = ANY(_addresses) + ; + + RETURN QUERY + WITH _all_utxos AS ( + SELECT + tx.id, + tx.hash, + tx_out.id as txo_id, + tx_out.address, + tx_out.value, + tx_out.index, + tx.block_id, + tx_out.data_hash, + tx_out.inline_datum_id, + tx_out.reference_script_id + FROM + tx_out + LEFT JOIN tx_in ON tx_in.tx_out_id = tx_out.tx_id + AND tx_in.tx_out_index = tx_out.index + INNER JOIN tx ON tx.id = tx_out.tx_id + WHERE + tx_in.id IS NULL + AND + tx_out.address = ANY(_addresses) + ) + + SELECT + ka.address, + COALESCE(SUM(au.value), '0')::text AS balance, + ka.stake_address, + ka.script_address, + CASE WHEN EXISTS ( + SELECT TRUE FROM _all_utxos aus WHERE aus.address = ka.address + ) THEN + JSON_AGG( + JSON_BUILD_OBJECT( + 'tx_hash', ENCODE(au.hash, 'hex'), + 'tx_index', au.index, + 'block_height', block.block_no, + 'block_time', EXTRACT(epoch from block.time)::integer, + 'value', au.value::text, + 'datum_hash', ENCODE(au.data_hash, 'hex'), + 'inline_datum', ( CASE WHEN au.inline_datum_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END + ), + 'reference_script', ( CASE WHEN au.reference_script_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END + ), + 'asset_list', COALESCE( + ( + SELECT + JSON_AGG(JSON_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTX.quantity::text + )) + FROM + ma_tx_out MTX + INNER JOIN multi_asset MA ON MA.id = MTX.ident + WHERE + MTX.tx_out_id = au.txo_id + ), + JSON_BUILD_ARRAY() + ) + ) + ) + ELSE + '[]'::json + END as utxo_set + FROM + _known_addresses ka + LEFT OUTER JOIN _all_utxos au ON au.address = ka.address + LEFT JOIN public.block ON block.id = au.block_id + LEFT JOIN datum ON datum.id = au.inline_datum_id + LEFT JOIN script ON script.id = au.reference_script_id + GROUP BY + ka.address, ka.stake_address, ka.script_address + ; + DROP TABLE _known_addresses; +END; +$$; + +COMMENT ON FUNCTION grest.address_info IS 'Get bulk address info - balance, associated stake address (if any) and UTXO set'; diff --git a/files/grest/rpc/address/address_txs.sql b/files/grest/rpc/address/address_txs.sql new file mode 100644 index 00000000..0d6c34ef --- /dev/null +++ b/files/grest/rpc/address/address_txs.sql @@ -0,0 +1,54 @@ +CREATE FUNCTION grest.address_txs (_addresses text[], _after_block_height integer DEFAULT 0) + RETURNS TABLE ( + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _tx_id_list bigint[]; +BEGIN + -- all tx_out & tx_in tx ids + SELECT INTO _tx_id_list ARRAY_AGG(tx_id) + FROM ( + SELECT + tx_id + FROM + tx_out + WHERE + address = ANY (_addresses) + -- + UNION + -- + SELECT + tx_in_id AS tx_id + FROM + tx_out + LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id + AND tx_out.index = tx_in.tx_out_index + WHERE + tx_in.tx_in_id IS NOT NULL + AND tx_out.address = ANY (_addresses) + ) AS tmp; + + RETURN QUERY + SELECT + DISTINCT(ENCODE(tx.hash, 'hex')) as tx_hash, + block.epoch_no, + block.block_no, + EXTRACT(epoch from block.time)::integer + FROM + public.tx + INNER JOIN public.block ON block.id = tx.block_id + WHERE + tx.id = ANY (_tx_id_list) + AND block.block_no >= _after_block_height + ORDER BY + block.block_no DESC; +END; +$$; + +COMMENT ON FUNCTION grest.address_txs IS 'Get the transaction hash list of a Cardano address array, optionally filtering after specified block height (inclusive).'; + diff --git a/files/grest/rpc/address/credential_txs.sql b/files/grest/rpc/address/credential_txs.sql new file mode 100644 index 00000000..c9ea8e16 --- /dev/null +++ b/files/grest/rpc/address/credential_txs.sql @@ -0,0 +1,64 @@ +CREATE FUNCTION grest.credential_txs (_payment_credentials text[], _after_block_height integer DEFAULT 0) + RETURNS TABLE ( + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _payment_cred_bytea bytea[]; + _tx_id_list bigint[]; +BEGIN + -- convert input _payment_credentials array into bytea array + SELECT INTO _payment_cred_bytea ARRAY_AGG(cred_bytea) + FROM ( + SELECT + DECODE(cred_hex, 'hex') AS cred_bytea + FROM + UNNEST(_payment_credentials) AS cred_hex + ) AS tmp; + + -- all tx_out & tx_in tx ids + SELECT INTO _tx_id_list ARRAY_AGG(tx_id) + FROM ( + SELECT + tx_id + FROM + tx_out + WHERE + payment_cred = ANY (_payment_cred_bytea) + -- + UNION + -- + SELECT + tx_in_id AS tx_id + FROM + tx_out + LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id + AND tx_out.index = tx_in.tx_out_index + WHERE + tx_in.tx_in_id IS NOT NULL + AND tx_out.payment_cred = ANY (_payment_cred_bytea) + ) AS tmp; + + RETURN QUERY + SELECT + DISTINCT(ENCODE(tx.hash, 'hex')) as tx_hash, + block.epoch_no, + block.block_no, + EXTRACT(epoch from block.time)::integer + FROM + public.tx + INNER JOIN public.block ON block.id = tx.block_id + WHERE + tx.id = ANY (_tx_id_list) + AND block.block_no >= _after_block_height + ORDER BY + block.block_no DESC; +END; +$$; + +COMMENT ON FUNCTION grest.address_txs IS 'Get the transaction hash list of a payment credentials array, optionally filtering after specified block height (inclusive).'; + diff --git a/files/grest/rpc/assets/asset_address_list.sql b/files/grest/rpc/assets/asset_address_list.sql new file mode 100644 index 00000000..6b02a1c9 --- /dev/null +++ b/files/grest/rpc/assets/asset_address_list.sql @@ -0,0 +1,42 @@ +CREATE FUNCTION grest.asset_address_list (_asset_policy text, _asset_name text default '') + RETURNS TABLE ( + payment_address varchar, + quantity text + ) LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _asset_name_decoded bytea; + _asset_id int; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + SELECT DECODE( + CASE WHEN _asset_name IS NULL + THEN '' + ELSE + _asset_name + END, + 'hex' + ) INTO _asset_name_decoded; + SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; + + RETURN QUERY + SELECT + TXO.ADDRESS, + SUM(MTX.QUANTITY)::text + FROM + MA_TX_OUT MTX + INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident + INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID + LEFT JOIN TX_IN ON TXO.TX_ID = TX_IN.TX_OUT_ID + AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint + WHERE + MA.id = _asset_id + AND TX_IN.TX_IN_ID IS NULL + GROUP BY + TXO.ADDRESS; +END; +$$; + +COMMENT ON FUNCTION grest.asset_address_list IS 'Get the list of all addresses containing a specific asset'; + diff --git a/files/grest/rpc/assets/asset_history.sql b/files/grest/rpc/assets/asset_history.sql new file mode 100644 index 00000000..5088d7a6 --- /dev/null +++ b/files/grest/rpc/assets/asset_history.sql @@ -0,0 +1,77 @@ +CREATE FUNCTION grest.asset_history (_asset_policy text, _asset_name text default '') + RETURNS TABLE ( + policy_id text, + asset_name text, + fingerprint character varying, + minting_txs json[] + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _asset_name_decoded bytea; +BEGIN + + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + + SELECT DECODE( + CASE WHEN _asset_name IS NULL + THEN '' + ELSE + _asset_name + END, + 'hex' + ) INTO _asset_name_decoded; + + RETURN QUERY + SELECT + _asset_policy, + _asset_name, + minting_data.fingerprint, + ARRAY_AGG( + JSON_BUILD_OBJECT( + 'tx_hash', minting_data.tx_hash, + 'block_time', minting_data.block_time, + 'quantity', minting_data.quantity, + 'metadata', minting_data.metadata + ) + ORDER BY minting_data.id DESC + ) + FROM ( + SELECT + ma.fingerprint, + tx.id, + ENCODE(tx.hash, 'hex') AS tx_hash, + EXTRACT(epoch from b.time)::integer as block_time, + mtm.quantity::text, + ( CASE WHEN TM.key IS NULL THEN JSON_BUILD_ARRAY() + ELSE + JSON_AGG( + JSON_BUILD_OBJECT( + 'key', TM.key::text, + 'json', TM.json + ) + ) + END + ) AS metadata + FROM + ma_tx_mint mtm + INNER JOIN multi_asset ma ON ma.id = mtm.ident + INNER JOIN tx ON tx.id = MTM.tx_id + INNER JOIN block b ON b.id = tx.block_id + LEFT JOIN tx_metadata TM ON TM.tx_id = tx.id + WHERE MA.policy = _asset_policy_decoded + AND MA.name = _asset_name_decoded + GROUP BY + ma.fingerprint, + tx.id, + b.time, + mtm.quantity, + TM.key + ) minting_data + GROUP BY + minting_data.fingerprint; +END; +$$; + +COMMENT ON FUNCTION grest.asset_history IS 'Get the mint/burn history of an asset'; diff --git a/files/grest/rpc/assets/asset_info.sql b/files/grest/rpc/assets/asset_info.sql new file mode 100644 index 00000000..e5109cb1 --- /dev/null +++ b/files/grest/rpc/assets/asset_info.sql @@ -0,0 +1,115 @@ +CREATE OR REPLACE FUNCTION grest.asset_info (_asset_policy text, _asset_name text default '') + RETURNS TABLE ( + policy_id text, + asset_name text, + asset_name_ascii text, + fingerprint character varying, + minting_tx_hash text, + total_supply text, + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, + minting_tx_metadata jsonb, + token_registry_metadata json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _asset_name_decoded bytea; + _asset_id int; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + + SELECT DECODE( + CASE WHEN _asset_name IS NULL + THEN '' + ELSE + _asset_name + END, + 'hex' + ) INTO _asset_name_decoded; + + SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; + + RETURN QUERY + SELECT + _asset_policy, + _asset_name, + ENCODE(_asset_name_decoded, 'escape'), + MA.fingerprint, + ( + SELECT + ENCODE(tx.hash, 'hex') + FROM + ma_tx_mint MTM + INNER JOIN tx ON tx.id = MTM.tx_id + WHERE + MTM.ident = _asset_id AND MTM.quantity > 0 + ORDER BY + MTM.tx_id ASC + LIMIT 1 + ) AS tx_hash, + minting_data.total_supply, + minting_data.mint_cnt, + minting_data.burn_cnt, + EXTRACT(epoch from minting_data.date)::integer, + ( + SELECT + JSONB_OBJECT_AGG( + key::text, + json + ) as minting_tx_metadata + FROM + ( + SELECT TM.key, TM.json, MAX(MTM.tx_id) + FROM tx_metadata TM + INNER JOIN ma_tx_mint MTM + ON TM.tx_id = MTM.tx_id + WHERE + MTM.ident = _asset_id + AND MTM.quantity > 0 + AND TM.JSON IS NOT NULL + GROUP BY TM.key, TM.json + ) x + ) AS minting_tx_metadata, + ( + SELECT + JSON_BUILD_OBJECT( + 'name', ARC.name, + 'description', ARC.description, + 'ticker', ARC.ticker, + 'url', ARC.url, + 'logo', ARC.logo, + 'decimals', ARC.decimals + ) as metadata + FROM + grest.asset_registry_cache ARC + WHERE + ARC.asset_policy = _asset_policy + AND + ARC.asset_name = _asset_name + LIMIT 1 + ) AS token_registry_metadata + FROM + multi_asset MA + LEFT JOIN LATERAL ( + SELECT + MIN(B.time) AS date, + SUM(MTM.quantity)::text AS total_supply, + SUM(CASE WHEN quantity > 0 then 1 else 0 end) AS mint_cnt, + SUM(CASE WHEN quantity < 0 then 1 else 0 end) AS burn_cnt + FROM + ma_tx_mint MTM + INNER JOIN tx ON tx.id = MTM.tx_id + INNER JOIN block B ON B.id = tx.block_id + WHERE + MTM.ident = MA.id + ) minting_data ON TRUE + WHERE + MA.id = _asset_id; +END; +$$; + +COMMENT ON FUNCTION grest.asset_info IS 'Get the information of an asset incl first minting & token registry metadata'; + diff --git a/files/grest/rpc/assets/asset_policy_info.sql b/files/grest/rpc/assets/asset_policy_info.sql new file mode 100644 index 00000000..ccfdc54f --- /dev/null +++ b/files/grest/rpc/assets/asset_policy_info.sql @@ -0,0 +1,114 @@ +CREATE FUNCTION grest.asset_policy_info (_asset_policy text) + RETURNS TABLE ( + asset_name text, + asset_name_ascii text, + fingerprint varchar, + minting_tx_metadata jsonb, + token_registry_metadata jsonb, + total_supply text, + creation_time integer + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _policy_asset_ids bigint[]; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + SELECT INTO _policy_asset_ids ARRAY_AGG(id) FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded; + + RETURN QUERY ( + WITH + minting_tx_metadatas AS ( + SELECT DISTINCT ON (MTM.ident) + MTM.ident, + JSONB_BUILD_OBJECT( + 'key', TM.key::text, + 'json', TM.json + ) AS metadata + FROM + tx_metadata TM + INNER JOIN ma_tx_mint MTM on MTM.tx_id = TM.tx_id + WHERE + MTM.ident = ANY(_policy_asset_ids) AND MTM.quantity > 0 + ORDER BY + MTM.ident, + TM.tx_id ASC + ), + token_registry_metadatas AS ( + SELECT DISTINCT ON (asset_policy, asset_name) + asset_policy, + ARC.asset_name, + JSONB_BUILD_OBJECT( + 'name', ARC.name, + 'description', ARC.description, + 'ticker', ARC.ticker, + 'url', ARC.url, + 'logo', ARC.logo, + 'decimals', ARC.decimals + ) as metadata + FROM + grest.asset_registry_cache ARC + WHERE + asset_policy = _asset_policy + ORDER BY + asset_policy, + ARC.asset_name + ), + total_supplies AS ( + SELECT + MTM.ident, + SUM(MTM.quantity)::text AS amount + FROM + ma_tx_mint MTM + WHERE + MTM.ident = ANY(_policy_asset_ids) + GROUP BY + MTM.ident + ), + creation_times AS ( + SELECT + MTM.ident, + MIN(B.time) AS date + FROM + ma_tx_mint MTM + INNER JOIN tx ON tx.id = MTM.tx_id + INNER JOIN block B ON B.id = tx.block_id + WHERE + MTM.ident = ANY(_policy_asset_ids) + GROUP BY + MTM.ident + ) + + SELECT + ENCODE(MA.name, 'hex') as asset_name, + ENCODE(MA.name, 'escape') as asset_name_ascii, + MA.fingerprint as fingerprint, + mtm.metadata as minting_tx_metadata, + trm.metadata as token_registry_metadata, + ts.amount::text as total_supply, + EXTRACT(epoch from ct.date)::integer + FROM + multi_asset MA + LEFT JOIN minting_tx_metadatas mtm ON mtm.ident = MA.id + LEFT JOIN token_registry_metadatas trm ON trm.asset_policy = _asset_policy + AND DECODE(trm.asset_name, 'hex') = MA.name + INNER JOIN total_supplies ts on ts.ident = MA.id + INNER JOIN creation_times ct ON ct.ident = MA.id + WHERE + MA.id = ANY(_policy_asset_ids) + GROUP BY + MA.policy, + MA.name, + MA.fingerprint, + MA.id, + mtm.metadata, + trm.metadata, + ts.amount, + ct.date + ); +END; +$$; + +COMMENT ON FUNCTION grest.asset_policy_info IS 'Get the asset information of all assets under a policy'; + diff --git a/files/grest/rpc/assets/asset_summary.sql b/files/grest/rpc/assets/asset_summary.sql new file mode 100644 index 00000000..914bfadb --- /dev/null +++ b/files/grest/rpc/assets/asset_summary.sql @@ -0,0 +1,81 @@ +CREATE FUNCTION grest.asset_summary (_asset_policy text, _asset_name text default '') + RETURNS TABLE ( + policy_id text, + asset_name text, + fingerprint character varying, + total_transactions bigint, + staked_wallets bigint, + unstaked_addresses bigint + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _asset_name_decoded bytea; + _asset_id int; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + SELECT DECODE( + CASE WHEN _asset_name IS NULL + THEN '' + ELSE + _asset_name + END, + 'hex' + ) INTO _asset_name_decoded; + SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; + +RETURN QUERY + with _asset_utxos as ( + SELECT + TXO.tx_id AS tx_id, + TXO.id AS tx_out_id, + TXO.index AS tx_out_idx, + TXO.address AS address, + TXO.stake_address_id AS sa_id + FROM + ma_tx_out MTO + INNER JOIN tx_out TXO ON TXO.id = MTO.tx_out_id + LEFT JOIN tx_in TXI ON TXI.tx_out_id = TXO.tx_id + WHERE + MTO.ident = _asset_id + AND + TXI.tx_out_id IS NULL) + + SELECT + _asset_policy, + _asset_name, + MA.fingerprint, + ( + SELECT + COUNT(DISTINCT(TXO.tx_id)) + FROM + ma_tx_out MTO + INNER JOIN tx_out TXO ON TXO.id = MTO.tx_out_id + WHERE + ident = _asset_id + ) AS total_transactions, + ( + SELECT + COUNT(DISTINCT(_asset_utxos.sa_id)) + FROM + _asset_utxos + WHERE + _asset_utxos.sa_id IS NOT NULL + ) AS staked_wallets, + ( + SELECT + COUNT(DISTINCT(_asset_utxos.address)) + FROM + _asset_utxos + WHERE + _asset_utxos.sa_id IS NULL + ) AS unstaked_addresses + FROM + multi_asset MA + WHERE + MA.id = _asset_id; +END; +$$; + +COMMENT ON FUNCTION grest.asset_summary IS 'Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance)'; diff --git a/files/grest/rpc/assets/asset_txs.sql b/files/grest/rpc/assets/asset_txs.sql new file mode 100644 index 00000000..a1c14771 --- /dev/null +++ b/files/grest/rpc/assets/asset_txs.sql @@ -0,0 +1,64 @@ +CREATE OR REPLACE FUNCTION grest.asset_txs ( + _asset_policy text, + _asset_name text default '', + _after_block_height integer DEFAULT 0, + _history boolean DEFAULT false +) + RETURNS TABLE ( + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _asset_name_decoded bytea; + _asset_id int; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + SELECT DECODE( + CASE WHEN _asset_name IS NULL + THEN '' + ELSE + _asset_name + END, + 'hex' + ) INTO _asset_name_decoded; + SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; + + RETURN QUERY + SELECT + ENCODE(tx_hashes.hash, 'hex') as tx_hash, + tx_hashes.epoch_no, + tx_hashes.block_no, + EXTRACT(epoch from tx_hashes.time)::integer + FROM ( + SELECT DISTINCT ON (tx.hash) + tx.hash, + block.epoch_no, + block.block_no, + block.time + FROM + ma_tx_out MTO + INNER JOIN tx_out TXO ON TXO.id = MTO.tx_out_id + INNER JOIN tx ON tx.id = TXO.tx_id + INNER JOIN block ON block.id = tx.block_id + LEFT JOIN tx_in TXI ON TXO.tx_id = TXI.tx_out_id + AND TXO.index::smallint = TXI.tx_out_index::smallint + WHERE + MTO.ident = _asset_id + AND block.block_no >= _after_block_height + AND (_history = true OR TXI.id IS NULL) + GROUP BY + ident, + tx.hash, + block.epoch_no, + block.block_no, + block.time + ) tx_hashes ORDER BY tx_hashes.block_no DESC; +END; +$$; + +COMMENT ON FUNCTION grest.asset_txs IS 'Get the list of all asset transaction hashes (newest first)'; diff --git a/files/grest/rpc/blocks/block_info.sql b/files/grest/rpc/blocks/block_info.sql new file mode 100644 index 00000000..45400f38 --- /dev/null +++ b/files/grest/rpc/blocks/block_info.sql @@ -0,0 +1,116 @@ +CREATE FUNCTION grest.block_info (_block_hashes text[]) + RETURNS TABLE ( + hash text, + epoch_no word31type, + abs_slot word63type, + epoch_slot word31type, + block_height word31type, + block_size word31type, + block_time integer, + tx_count bigint, + vrf_key varchar, + op_cert text, + op_cert_counter word63type, + pool varchar, + proto_major word31type, + proto_minor word31type, + total_output text, + total_fees text, + num_confirmations integer, + parent_hash text, + child_hash text + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _block_hashes_bytea bytea[]; + _block_id_list bigint[]; + _curr_block_no word31type; +BEGIN + SELECT max(block_no) INTO _curr_block_no FROM block b; + + -- convert input _block_hashes array into bytea array + SELECT INTO _block_hashes_bytea ARRAY_AGG(hashes_bytea) + FROM ( + SELECT + DECODE(hashes_hex, 'hex') AS hashes_bytea + FROM + UNNEST(_block_hashes) AS hashes_hex + ) AS tmp; + + -- all block ids + SELECT INTO _block_id_list ARRAY_AGG(id) + FROM ( + SELECT + id + FROM + block + WHERE block.hash = ANY (_block_hashes_bytea) + ) AS tmp; + + RETURN QUERY + SELECT + ENCODE(B.hash, 'hex') AS hash, + B.epoch_no AS epoch, + B.slot_no AS abs_slot, + B.epoch_slot_no AS epoch_slot, + B.block_no AS block_height, + B.size AS block_size, + EXTRACT(epoch from B.time)::integer AS block_time, + B.tx_count, + B.vrf_key, + ENCODE(B.op_cert::bytea, 'hex') as op_cert, + B.op_cert_counter, + PH.view AS pool, + B.proto_major, + B.proto_minor, + block_data.total_output::text, + block_data.total_fees::text, + (_curr_block_no - B.block_no) AS num_confirmations, + ( + SELECT + ENCODE(tB.hash::bytea, 'hex') + FROM + block tB + WHERE + block_no = b.block_no - 1 + LIMIT 1 + ) AS parent_hash, + ( + SELECT + ENCODE(tB.hash::bytea, 'hex') + FROM + block tB + WHERE + block_no = b.block_no + 1 + LIMIT 1 + ) AS child_hash + FROM + block B + LEFT JOIN slot_leader SL ON SL.id = B.slot_leader_id + LEFT JOIN pool_hash PH ON PH.id = SL.pool_hash_id + LEFT JOIN LATERAL ( + SELECT + SUM(tx_data.total_output) AS total_output, + SUM(tx.fee) AS total_fees + FROM + tx + JOIN LATERAL ( + SELECT + SUM(tx_out.value) AS total_output + FROM + tx_out + WHERE + tx_out.tx_id = tx.id + ) tx_data ON TRUE + WHERE + tx.block_id = b.id + ) block_data ON TRUE + WHERE + B.id = ANY (_block_id_list) + AND B.block_no IS NOT NULL; +END; +$$; + +COMMENT ON FUNCTION grest.block_info IS 'Get detailed information about list of block hashes'; + diff --git a/files/grest/rpc/blocks/block_txs.sql b/files/grest/rpc/blocks/block_txs.sql new file mode 100644 index 00000000..7710dbb8 --- /dev/null +++ b/files/grest/rpc/blocks/block_txs.sql @@ -0,0 +1,43 @@ +CREATE FUNCTION grest.block_txs (_block_hashes text[]) + RETURNS TABLE ( + block_hash text, + tx_hashes text[] + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _block_hashes_bytea bytea[]; + _BLOCK_IDS integer[]; +BEGIN + SELECT INTO _block_hashes_bytea + ARRAY_AGG(block_hashes_bytea) + FROM ( + SELECT + DECODE(hex, 'hex') AS block_hashes_bytea + FROM + UNNEST(_block_hashes) AS hex + ) AS tmp; + + SELECT INTO _BLOCK_IDS + ARRAY_AGG(B.ID) + FROM + public.BLOCK B + WHERE + B.HASH = ANY(_block_hashes_bytea); + + RETURN QUERY + SELECT + encode(b.hash, 'hex'), + ARRAY_AGG(ENCODE(TX.HASH::bytea, 'hex')) + FROM + public.BLOCK B + INNER JOIN public.TX ON TX.BLOCK_ID = B.ID + WHERE + B.ID = ANY(_BLOCK_IDS) + GROUP BY + B.hash; +END; +$$; + +COMMENT ON FUNCTION grest.block_txs IS 'Get all transactions contained in given blocks'; + diff --git a/files/grest/rpc/epoch/epoch_block_protocols.sql b/files/grest/rpc/epoch/epoch_block_protocols.sql new file mode 100644 index 00000000..1974bcc1 --- /dev/null +++ b/files/grest/rpc/epoch/epoch_block_protocols.sql @@ -0,0 +1,38 @@ +CREATE FUNCTION grest.epoch_block_protocols (_epoch_no numeric DEFAULT NULL) + RETURNS TABLE ( + proto_major word31type, + proto_minor word31type, + blocks bigint + ) + LANGUAGE PLPGSQL + AS $$ +BEGIN + IF _epoch_no IS NOT NULL THEN + RETURN QUERY + SELECT + b.proto_major, + b.proto_minor, + count(b.*) + FROM + block b + WHERE + b.epoch_no = _epoch_no::word31type + GROUP BY + b.proto_major, b.proto_minor; + ELSE + RETURN QUERY + SELECT + b.proto_major, + b.proto_minor, + count(b.*) + FROM + block b + WHERE + b.epoch_no = (SELECT MAX(no) FROM epoch) + GROUP BY + b.proto_major, b.proto_minor; + END IF; +END; +$$; + +COMMENT ON FUNCTION grest.epoch_block_protocols IS 'Get the information about block protocol distribution in epoch'; diff --git a/files/grest/rpc/epoch/epoch_info.sql b/files/grest/rpc/epoch/epoch_info.sql new file mode 100644 index 00000000..757c7fa5 --- /dev/null +++ b/files/grest/rpc/epoch/epoch_info.sql @@ -0,0 +1,54 @@ +CREATE FUNCTION grest.epoch_info (_epoch_no numeric DEFAULT NULL) + RETURNS TABLE ( + epoch_no word31type, + out_sum text, + fees text, + tx_count word31type, + blk_count word31type, + start_time integer, + end_time integer, + first_block_time integer, + last_block_time integer, + active_stake text, + total_rewards text, + avg_blk_reward text + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + shelley_epoch_duration numeric := (select epochlength::numeric * slotlength::numeric as epochduration from grest.genesis); + shelley_ref_epoch numeric := (select (ep.epoch_no::numeric + 1) from epoch_param ep ORDER BY ep.epoch_no LIMIT 1); + shelley_ref_time numeric := (select ei.i_first_block_time from grest.epoch_info_cache ei where ei.epoch_no = shelley_ref_epoch); +BEGIN + RETURN QUERY + SELECT + ei.epoch_no, + ei.i_out_sum::text AS tx_output_sum, + ei.i_fees::text AS tx_fees_sum, + ei.i_tx_count AS tx_count, + ei.i_blk_count AS blk_count, + CASE WHEN ei.epoch_no < shelley_ref_epoch THEN + ei.i_first_block_time::integer + ELSE + (shelley_ref_time + (ei.epoch_no - shelley_ref_epoch) * shelley_epoch_duration)::integer + END AS start_time, + CASE WHEN ei.epoch_no < shelley_ref_epoch THEN + (ei.i_first_block_time + shelley_epoch_duration)::integer + ELSE + (shelley_ref_time + ((ei.epoch_no + 1) - shelley_ref_epoch) * shelley_epoch_duration)::integer + END AS end_time, + ei.i_first_block_time::integer AS first_block_time, + ei.i_last_block_time::integer AS last_block_time, + eas.amount::text AS active_stake, + ei.i_total_rewards::text AS total_rewards, + ei.i_avg_blk_reward::text AS avg_blk_reward + FROM + grest.epoch_info_cache ei + LEFT JOIN grest.EPOCH_ACTIVE_STAKE_CACHE eas ON eas.epoch_no = ei.epoch_no + WHERE + ei.epoch_no::text LIKE CASE WHEN _epoch_no IS NULL THEN '%' ELSE _epoch_no::text END; +END; +$$; + +COMMENT ON FUNCTION grest.epoch_info IS 'Get the epoch information, all epochs if no epoch specified'; + diff --git a/files/grest/rpc/epoch/epoch_params.sql b/files/grest/rpc/epoch/epoch_params.sql new file mode 100644 index 00000000..5d8ad31f --- /dev/null +++ b/files/grest/rpc/epoch/epoch_params.sql @@ -0,0 +1,121 @@ +CREATE FUNCTION grest.epoch_params (_epoch_no numeric DEFAULT NULL) + RETURNS TABLE ( + epoch_no word31type, + min_fee_a word31type, + min_fee_b word31type, + max_block_size word31type, + max_tx_size word31type, + max_bh_size word31type, + key_deposit text, + pool_deposit text, + max_epoch word31type, + optimal_pool_count word31type, + influence double precision, + monetary_expand_rate double precision, + treasury_growth_rate double precision, + decentralisation double precision, + extra_entropy text, + protocol_major word31type, + protocol_minor word31type, + min_utxo_value text, + min_pool_cost text, + nonce text, + block_hash text, + cost_models character varying, + price_mem double precision, + price_step double precision, + max_tx_ex_mem word64type, + max_tx_ex_steps word64type, + max_block_ex_mem word64type, + max_block_ex_steps word64type, + max_val_size word64type, + collateral_percent word31type, + max_collateral_inputs word31type, + coins_per_utxo_size text) + LANGUAGE PLPGSQL + AS $$ +BEGIN + IF _epoch_no IS NULL THEN + RETURN QUERY + SELECT + ei.epoch_no, + ei.p_min_fee_a AS min_fee_a, + ei.p_min_fee_b AS min_fee_b, + ei.p_max_block_size AS max_block_size, + ei.p_max_tx_size AS max_tx_size, + ei.p_max_bh_size AS max_bh_size, + ei.p_key_deposit::text AS key_deposit, + ei.p_pool_deposit::text AS pool_deposit, + ei.p_max_epoch AS max_epoch, + ei.p_optimal_pool_count AS optimal_pool_count, + ei.p_influence AS influence, + ei.p_monetary_expand_rate AS monetary_expand_rate, + ei.p_treasury_growth_rate AS treasury_growth_rate, + ei.p_decentralisation AS decentralisation, + ei.p_extra_entropy AS extra_entropy, + ei.p_protocol_major AS protocol_major, + ei.p_protocol_minor AS protocol_minor, + ei.p_min_utxo_value::text AS min_utxo_value, + ei.p_min_pool_cost::text AS min_pool_cost, + ei.p_nonce AS nonce, + ei.p_block_hash AS block_hash, + ei.p_cost_models AS cost_models, + ei.p_price_mem AS price_mem, + ei.p_price_step AS price_step, + ei.p_max_tx_ex_mem AS max_tx_ex_mem, + ei.p_max_tx_ex_steps AS max_tx_ex_steps, + ei.p_max_block_ex_mem AS max_block_ex_mem, + ei.p_max_block_ex_steps AS max_block_ex_steps, + ei.p_max_val_size AS max_val_size, + ei.p_collateral_percent AS collateral_percent, + ei.p_max_collateral_inputs AS max_collateral_inputs, + ei.p_coins_per_utxo_size::text AS coins_per_utxo_size + FROM + grest.epoch_info_cache ei + ORDER BY + ei.epoch_no DESC; + ELSE + RETURN QUERY + SELECT + ei.epoch_no, + ei.p_min_fee_a AS min_fee_a, + ei.p_min_fee_b AS min_fee_b, + ei.p_max_block_size AS max_block_size, + ei.p_max_tx_size AS max_tx_size, + ei.p_max_bh_size AS max_bh_size, + ei.p_key_deposit::text AS key_deposit, + ei.p_pool_deposit::text AS pool_deposit, + ei.p_max_epoch AS max_epoch, + ei.p_optimal_pool_count AS optimal_pool_count, + ei.p_influence AS influence, + ei.p_monetary_expand_rate AS monetary_expand_rate, + ei.p_treasury_growth_rate AS treasury_growth_rate, + ei.p_decentralisation AS decentralisation, + ei.p_extra_entropy AS extra_entropy, + ei.p_protocol_major AS protocol_major, + ei.p_protocol_minor AS protocol_minor, + ei.p_min_utxo_value::text AS min_utxo_value, + ei.p_min_pool_cost::text AS min_pool_cost, + ei.p_nonce AS nonce, + ei.p_block_hash AS block_hash, + ei.p_cost_models AS cost_models, + ei.p_price_mem AS price_mem, + ei.p_price_step AS price_step, + ei.p_max_tx_ex_mem AS max_tx_ex_mem, + ei.p_max_tx_ex_steps AS max_tx_ex_steps, + ei.p_max_block_ex_mem AS max_block_ex_mem, + ei.p_max_block_ex_steps AS max_block_ex_steps, + ei.p_max_val_size AS max_val_size, + ei.p_collateral_percent AS collateral_percent, + ei.p_max_collateral_inputs AS max_collateral_inputs, + ei.p_coins_per_utxo_size::text AS coins_per_utxo_size + FROM + grest.epoch_info_cache ei + WHERE + ei.epoch_no = _epoch_no; + END IF; +END; +$$; + +COMMENT ON FUNCTION grest.epoch_params IS 'Get the epoch parameters, all epochs if no epoch specified'; + diff --git a/files/grest/rpc/pool/pool_blocks.sql b/files/grest/rpc/pool/pool_blocks.sql new file mode 100644 index 00000000..1f3836d5 --- /dev/null +++ b/files/grest/rpc/pool/pool_blocks.sql @@ -0,0 +1,36 @@ +CREATE FUNCTION grest.pool_blocks (_pool_bech32 text, _epoch_no word31type DEFAULT NULL) + RETURNS TABLE ( + epoch_no word31type, + epoch_slot word31type, + abs_slot word63type, + block_height word31type, + block_hash text, + block_time integer + ) + LANGUAGE plpgsql + AS $$ +BEGIN + RETURN query + SELECT + b.epoch_no, + b.epoch_slot_no as epoch_slot, + b.slot_no as abs_slot, + b.block_no as block_height, + encode(b.hash::bytea, 'hex'), + EXTRACT(epoch from b.time)::integer + FROM + public.block b + INNER JOIN + public.slot_leader AS sl ON b.slot_leader_id = sl.id + WHERE + sl.pool_hash_id = (SELECT pool_hash_id FROM grest.pool_info_cache WHERE pool_id_bech32 = _pool_bech32 ORDER BY tx_id DESC LIMIT 1) + AND + ( + _epoch_no IS NULL + OR + b.epoch_no = _epoch_no + ); +END; +$$; + +COMMENT ON FUNCTION grest.pool_blocks IS 'Return information about blocks minted by a given pool in current epoch (or epoch nbr if provided)'; diff --git a/files/grest/rpc/pool/pool_delegators.sql b/files/grest/rpc/pool/pool_delegators.sql new file mode 100644 index 00000000..2418b8ca --- /dev/null +++ b/files/grest/rpc/pool/pool_delegators.sql @@ -0,0 +1,47 @@ +CREATE FUNCTION grest.pool_delegators (_pool_bech32 text) + RETURNS TABLE ( + stake_address character varying, + amount text, + active_epoch_no bigint, + latest_delegation_tx_hash text + ) + LANGUAGE plpgsql + AS $$ + #variable_conflict use_column +DECLARE + _pool_id bigint; +BEGIN + RETURN QUERY + WITH + _all_delegations AS ( + SELECT + SA.id AS stake_address_id, + SDC.stake_address, + ( + CASE WHEN SDC.total_balance >= 0 + THEN SDC.total_balance + ELSE 0 + END + ) AS total_balance + FROM + grest.stake_distribution_cache AS SDC + INNER JOIN public.stake_address SA ON SA.view = SDC.stake_address + WHERE + SDC.pool_id = _pool_bech32 + ) + + SELECT DISTINCT ON (AD.stake_address) + AD.stake_address, + AD.total_balance::text, + D.active_epoch_no, + ENCODE(tx.hash, 'hex') + FROM + _all_delegations AS AD + INNER JOIN public.delegation D ON D.addr_id = AD.stake_address_id + INNER JOIN public.tx ON tx.id = D.tx_id + ORDER BY + AD.stake_address, D.tx_id DESC; +END; +$$; + +COMMENT ON FUNCTION grest.pool_delegators IS 'Return information about live delegators for a given pool.'; diff --git a/files/grest/rpc/pool/pool_delegators_history.sql b/files/grest/rpc/pool/pool_delegators_history.sql new file mode 100644 index 00000000..4a33e2bd --- /dev/null +++ b/files/grest/rpc/pool/pool_delegators_history.sql @@ -0,0 +1,47 @@ +CREATE FUNCTION grest.pool_delegators_history (_pool_bech32 text, _epoch_no word31type DEFAULT NULL) + RETURNS TABLE ( + stake_address character varying, + amount text, + epoch_no word31type + ) + LANGUAGE plpgsql + AS $$ + #variable_conflict use_column +DECLARE + _pool_id bigint; +BEGIN + SELECT id INTO _pool_id FROM pool_hash WHERE pool_hash.view = _pool_bech32; + + IF _epoch_no IS NULL THEN + RETURN QUERY + SELECT + SA.view, + ES.amount::text, + ES.epoch_no + FROM + public.epoch_stake ES + INNER JOIN public.stake_address SA ON ES.addr_id = SA.id + WHERE + ES.pool_id = _pool_id + ORDER BY + ES.epoch_no DESC, ES.amount DESC; + ELSE + RETURN QUERY + SELECT + SA.view, + ES.amount::text, + ES.epoch_no + FROM + public.epoch_stake ES + INNER JOIN public.stake_address SA ON ES.addr_id = SA.id + WHERE + ES.pool_id = _pool_id + AND + ES.epoch_no = _epoch_no + ORDER BY + ES.amount DESC; + END IF; +END; +$$; + +COMMENT ON FUNCTION grest.pool_delegators IS 'Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided.'; diff --git a/files/grest/rpc/pool/pool_history.sql b/files/grest/rpc/pool/pool_history.sql new file mode 100644 index 00000000..61864341 --- /dev/null +++ b/files/grest/rpc/pool/pool_history.sql @@ -0,0 +1,35 @@ +CREATE OR REPLACE FUNCTION grest.pool_history (_pool_bech32 text, _epoch_no word31type DEFAULT NULL) + RETURNS TABLE ( + epoch_no bigint, + active_stake text, + active_stake_pct numeric, + saturation_pct numeric, + block_cnt bigint, + delegator_cnt bigint, + margin double precision, + fixed_cost text, + pool_fees text, + deleg_rewards text, + epoch_ros numeric + ) + LANGUAGE plpgsql + AS $$ + #variable_conflict use_column +DECLARE + +BEGIN + + RETURN QUERY + SELECT epoch_no, active_stake::text, active_stake_pct, saturation_pct, block_cnt, + delegator_cnt, pool_fee_variable as margin, coalesce(pool_fee_fixed, 0)::text as fixed_cost, + coalesce(pool_fees, 0)::text, coalesce(deleg_rewards, 0)::text, coalesce(epoch_ros, 0) + FROM grest.pool_history_cache phc + WHERE phc.pool_id = _pool_bech32 and + (_epoch_no is null or + phc.epoch_no = _epoch_no) + ORDER by phc.epoch_no desc; + +END; +$$; + +COMMENT ON FUNCTION grest.pool_history IS 'Pool block production and reward history for a given epoch (or all epochs if not specified)'; diff --git a/files/grest/rpc/pool/pool_info.sql b/files/grest/rpc/pool/pool_info.sql new file mode 100644 index 00000000..335f9ae5 --- /dev/null +++ b/files/grest/rpc/pool/pool_info.sql @@ -0,0 +1,171 @@ +CREATE FUNCTION grest.pool_info (_pool_bech32_ids text[]) + RETURNS TABLE ( + pool_id_bech32 character varying, + pool_id_hex text, + active_epoch_no bigint, + vrf_key_hash text, + margin double precision, + fixed_cost text, + pledge text, + reward_addr character varying, + owners character varying [], + relays jsonb [], + meta_url character varying, + meta_hash text, + meta_json jsonb, + pool_status text, + retiring_epoch word31type, + op_cert text, + op_cert_counter word63type, + active_stake text, + sigma numeric, + block_count numeric, + live_pledge text, + live_stake text, + live_delegators bigint, + live_saturation numeric + ) + LANGUAGE plpgsql + AS $$ + #variable_conflict use_column +DECLARE + _epoch_no bigint; + _saturation_limit bigint; +BEGIN + SELECT MAX(epoch.no) INTO _epoch_no FROM public.epoch; + + SELECT FLOOR(supply::bigint / ( + SELECT p_optimal_pool_count + FROM grest.epoch_info_cache + WHERE epoch_no = _epoch_no + ))::bigint INTO _saturation_limit FROM grest.totals(_epoch_no); + + RETURN QUERY + WITH + _all_pool_info AS ( + SELECT DISTINCT ON (pic.pool_id_bech32) + * + FROM + grest.pool_info_cache AS pic + WHERE + pic.pool_id_bech32 = ANY(SELECT UNNEST(_pool_bech32_ids)) + ORDER BY + pic.pool_id_bech32, pic.tx_id DESC + ) + + SELECT + api.pool_id_bech32, + api.pool_id_hex, + api.active_epoch_no, + api.vrf_key_hash, + api.margin, + api.fixed_cost::text, + api.pledge::text, + api.reward_addr, + api.owners, + api.relays, + api.meta_url, + api.meta_hash, + offline_data.json, + api.pool_status, + api.retiring_epoch, + ENCODE(block_data.op_cert::bytea, 'hex'), + block_data.op_cert_counter, + active_stake.as_sum::text, + active_stake.as_sum / epoch_stake.es_sum, + block_data.cnt, + live.pledge::text, + live.stake::text, + live.delegators, + ROUND((live.stake / _saturation_limit) * 100, 2) + FROM + _all_pool_info AS api + LEFT JOIN LATERAL ( + ( + SELECT + pod.json + FROM + public.pool_offline_data AS pod + WHERE + pod.pool_id = api.pool_hash_id + AND + pod.pmr_id = api.meta_id + ) + UNION ALL + ( + SELECT + pod.json + FROM + public.pool_offline_data AS pod + WHERE + pod.pool_id = api.pool_hash_id + AND + pod.json IS NOT NULL + ORDER BY + pod.pmr_id DESC + ) + LIMIT 1 + ) offline_data ON TRUE + LEFT JOIN LATERAL ( + SELECT + SUM(COUNT(b.id)) OVER () AS cnt, + b.op_cert, + b.op_cert_counter + FROM + public.block AS b + INNER JOIN + public.slot_leader AS sl ON b.slot_leader_id = sl.id + WHERE + sl.pool_hash_id = api.pool_hash_id + GROUP BY + b.op_cert, + b.op_cert_counter + ORDER BY + b.op_cert_counter DESC + LIMIT 1 + ) block_data ON TRUE + LEFT JOIN LATERAL( + SELECT + amount::lovelace AS as_sum + FROM + grest.pool_active_stake_cache AS pasc + WHERE + pasc.pool_id = api.pool_id_bech32 + AND + pasc.epoch_no = _epoch_no + ) active_stake ON TRUE + LEFT JOIN LATERAL( + SELECT + amount::lovelace AS es_sum + FROM + grest.epoch_active_stake_cache AS easc + WHERE + easc.epoch_no = _epoch_no + ) epoch_stake ON TRUE + LEFT JOIN LATERAL( + SELECT + CASE WHEN api.pool_status = 'retired' + THEN NULL + ELSE + SUM ( + CASE WHEN total_balance >= 0 + THEN total_balance + ELSE 0 + END + )::lovelace + END AS stake, + COUNT (stake_address) AS delegators, + CASE WHEN api.pool_status = 'retired' + THEN NULL + ELSE + SUM (CASE WHEN sdc.stake_address = ANY (api.owners) THEN total_balance ELSE 0 END)::lovelace + END AS pledge + FROM + grest.stake_distribution_cache AS sdc + WHERE + sdc.pool_id = api.pool_id_bech32 + ) live ON TRUE; +END; +$$; + +COMMENT ON FUNCTION grest.pool_info IS 'Current pool status and details for a specified list of pool ids'; diff --git a/files/grest/rpc/pool/pool_list.sql b/files/grest/rpc/pool/pool_list.sql new file mode 100644 index 00000000..ad4d7987 --- /dev/null +++ b/files/grest/rpc/pool/pool_list.sql @@ -0,0 +1,37 @@ +CREATE FUNCTION grest.pool_list () + RETURNS TABLE ( + pool_id_bech32 character varying, + ticker character varying) + LANGUAGE plpgsql + AS $$ + # variable_conflict use_column +BEGIN + + RETURN QUERY ( + WITH + -- Get last pool update for each pool + _pool_updates AS ( + SELECT + DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, + pod.ticker_name, + pic.pool_status + FROM + grest.pool_info_cache AS pic + LEFT JOIN public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id + ORDER BY + pic.pool_id_bech32, + pic.tx_id DESC + ) + + SELECT + pool_id_bech32, + ticker_name + FROM + _pool_updates + WHERE + pool_status != 'retired' + + ); + +END; +$$; diff --git a/files/grest/rpc/pool/pool_metadata.sql b/files/grest/rpc/pool/pool_metadata.sql new file mode 100644 index 00000000..103fda50 --- /dev/null +++ b/files/grest/rpc/pool/pool_metadata.sql @@ -0,0 +1,34 @@ +CREATE FUNCTION grest.pool_metadata (_pool_bech32_ids text[] DEFAULT null) + RETURNS TABLE ( + pool_id_bech32 character varying, + meta_url character varying, + meta_hash text, + meta_json jsonb + ) + LANGUAGE plpgsql + AS $$ + #variable_conflict use_column +BEGIN + RETURN QUERY + SELECT + DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, + pic.meta_url, + pic.meta_hash, + pod.json + FROM + grest.pool_info_cache AS pic + LEFT JOIN + public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id + WHERE + pic.pool_status != 'retired' + AND + CASE + WHEN _pool_bech32_ids IS NULL THEN true + WHEN _pool_bech32_ids IS NOT NULL THEN pic.pool_id_bech32 = ANY(SELECT UNNEST(_pool_bech32_ids)) + END + ORDER BY + pic.pool_id_bech32, pic.tx_id DESC; +END; +$$; + +COMMENT ON FUNCTION grest.pool_metadata IS 'Metadata(on & off-chain) for all currently registered/retiring (not retired) pools'; diff --git a/files/grest/rpc/pool/pool_relays.sql b/files/grest/rpc/pool/pool_relays.sql new file mode 100644 index 00000000..43b95e4f --- /dev/null +++ b/files/grest/rpc/pool/pool_relays.sql @@ -0,0 +1,23 @@ +CREATE FUNCTION grest.pool_relays () + RETURNS TABLE ( + pool_id_bech32 character varying, + relays jsonb [] + ) + LANGUAGE plpgsql + AS $$ + #variable_conflict use_column +BEGIN + RETURN QUERY + SELECT + DISTINCT ON (pool_id_bech32) pool_id_bech32, + relays + FROM + grest.pool_info_cache + WHERE + pool_status != 'retired' + ORDER BY + pool_id_bech32, tx_id DESC; +END; +$$; + +COMMENT ON FUNCTION grest.pool_relays IS 'A list of registered relays for all currently registered/retiring (not retired) pools'; diff --git a/files/grest/rpc/pool/pool_stake_snapshot.sql b/files/grest/rpc/pool/pool_stake_snapshot.sql new file mode 100644 index 00000000..643fea05 --- /dev/null +++ b/files/grest/rpc/pool/pool_stake_snapshot.sql @@ -0,0 +1,45 @@ +CREATE FUNCTION grest.pool_stake_snapshot (_pool_bech32 text) + RETURNS TABLE ( + snapshot text, + epoch_no bigint, + nonce text, + pool_stake text, + active_stake text + ) + LANGUAGE plpgsql + AS $$ +DECLARE + _epoch_no bigint; + _mark bigint; + _set bigint; + _go bigint; +BEGIN + SELECT MAX(epoch.no) INTO _epoch_no FROM public.epoch; + _mark := (_epoch_no+1); + _set := (_epoch_no); + _go := (_epoch_no-1); + + RETURN QUERY + SELECT + CASE + WHEN (pasc.epoch_no = _mark) THEN 'Mark' + WHEN (pasc.epoch_no = _set) THEN 'Set' + ELSE 'Go' + END AS snapshot, + pasc.epoch_no, + eic.p_nonce, + pasc.amount::text, + easc.amount::text + FROM + grest.pool_active_stake_cache pasc + INNER JOIN grest.epoch_active_stake_cache easc ON easc.epoch_no = pasc.epoch_no + LEFT JOIN grest.epoch_info_cache eic ON eic.epoch_no = pasc.epoch_no + WHERE + pasc.pool_id = _pool_bech32 + AND pasc.epoch_no BETWEEN _go AND _mark + ORDER BY + pasc.epoch_no; +END; +$$; + +COMMENT ON FUNCTION grest.pool_stake_snapshot IS 'Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation'; \ No newline at end of file diff --git a/files/grest/rpc/pool/pool_updates.sql b/files/grest/rpc/pool/pool_updates.sql new file mode 100644 index 00000000..1170b2c4 --- /dev/null +++ b/files/grest/rpc/pool/pool_updates.sql @@ -0,0 +1,56 @@ +CREATE FUNCTION grest.pool_updates (_pool_bech32 text DEFAULT NULL) + RETURNS TABLE ( + tx_hash text, + block_time integer, + pool_id_bech32 character varying, + pool_id_hex text, + active_epoch_no bigint, + vrf_key_hash text, + margin double precision, + fixed_cost text, + pledge text, + reward_addr character varying, + owners character varying [], + relays jsonb [], + meta_url character varying, + meta_hash text, + meta_json jsonb, + pool_status text, + retiring_epoch word31type + ) + LANGUAGE plpgsql + AS $$ + #variable_conflict use_column +BEGIN + RETURN QUERY + SELECT + tx_hash, + block_time::integer, + pool_id_bech32, + pool_id_hex, + active_epoch_no, + vrf_key_hash, + margin, + fixed_cost::text, + pledge::text, + reward_addr, + owners, + relays, + meta_url, + meta_hash, + pod.json, + pool_status, + retiring_epoch + FROM + grest.pool_info_cache pic + LEFT JOIN public.pool_offline_data pod ON pod.id = pic.meta_id + WHERE + _pool_bech32 IS NULL + OR + pool_id_bech32 = _pool_bech32 + ORDER BY + tx_id DESC; +END; +$$; + +COMMENT ON FUNCTION grest.pool_updates IS 'Return all pool_updates for all pools or only updates for specific pool if specified'; diff --git a/files/grest/rpc/script/datum_info.sql b/files/grest/rpc/script/datum_info.sql new file mode 100644 index 00000000..bf652e73 --- /dev/null +++ b/files/grest/rpc/script/datum_info.sql @@ -0,0 +1,28 @@ +CREATE FUNCTION grest.datum_info (_datum_hashes text[]) + RETURNS TABLE ( + hash text, + value jsonb, + bytes text + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _datum_hashes_decoded bytea[]; +BEGIN + SELECT INTO _datum_hashes_decoded + ARRAY_AGG(DECODE(d_hash, 'hex')) + FROM UNNEST(_datum_hashes) AS d_hash; + + RETURN QUERY + SELECT + ENCODE(d.hash, 'hex'), + d.value, + ENCODE(d.bytes, 'hex') + FROM + datum d + WHERE + d.hash = ANY(_datum_hashes_decoded); +END; +$$; + +COMMENT ON FUNCTION grest.datum_info IS 'Get information about a given data from hashes.'; diff --git a/files/grest/rpc/script/native_script_list.sql b/files/grest/rpc/script/native_script_list.sql new file mode 100644 index 00000000..a7f9eac3 --- /dev/null +++ b/files/grest/rpc/script/native_script_list.sql @@ -0,0 +1,23 @@ +CREATE FUNCTION grest.native_script_list () + RETURNS TABLE ( + script_hash text, + creation_tx_hash text, + type scripttype, + script jsonb + ) +LANGUAGE PLPGSQL AS +$$ +BEGIN + RETURN QUERY + SELECT + ENCODE(script.hash, 'hex'), + ENCODE(tx.hash, 'hex'), + script.type, + script.json + FROM script + INNER JOIN tx ON tx.id = script.tx_id + WHERE script.type IN ('timelock', 'multisig'); +END; +$$; + +COMMENT ON FUNCTION grest.native_script_list IS 'Get a list of all native(multisig/timelock) script hashes with creation tx hash, type and script in json format.'; diff --git a/files/grest/rpc/script/plutus_script_list.sql b/files/grest/rpc/script/plutus_script_list.sql new file mode 100644 index 00000000..63d05717 --- /dev/null +++ b/files/grest/rpc/script/plutus_script_list.sql @@ -0,0 +1,19 @@ +CREATE FUNCTION grest.plutus_script_list () + RETURNS TABLE ( + script_hash text, + creation_tx_hash text + ) +LANGUAGE PLPGSQL AS +$$ +BEGIN + RETURN QUERY + SELECT + ENCODE(script.hash, 'hex') as script_hash, + ENCODE(tx.hash, 'hex') as creation_tx_hash + FROM script + INNER JOIN tx ON tx.id = script.tx_id + WHERE script.type IN ('plutusV1', 'plutusV2'); +END; +$$; + +COMMENT ON FUNCTION grest.plutus_script_list IS 'Get a list of all plutus script hashes with creation tx hash.'; diff --git a/files/grest/rpc/script/script_redeemers.sql b/files/grest/rpc/script/script_redeemers.sql new file mode 100644 index 00000000..58261ecb --- /dev/null +++ b/files/grest/rpc/script/script_redeemers.sql @@ -0,0 +1,42 @@ +CREATE FUNCTION grest.script_redeemers (_script_hash text) + RETURNS TABLE ( + script_hash text, + redeemers json + ) +LANGUAGE PLPGSQL AS +$$ +DECLARE _script_hash_bytea bytea; +BEGIN +SELECT INTO _script_hash_bytea DECODE(_script_hash, 'hex'); +RETURN QUERY +select _script_hash, + JSON_AGG( + JSON_BUILD_OBJECT( + 'tx_hash', + ENCODE(tx.hash, 'hex'), + 'tx_index', + redeemer.index, + 'unit_mem', + redeemer.unit_mem, + 'unit_steps', + redeemer.unit_steps, + 'fee', + redeemer.fee::text, + 'purpose', + redeemer.purpose, + 'datum_hash', + ENCODE(rd.hash, 'hex'), + 'datum_value', + rd.value + -- extra bytes field available in rd. table here + ) + ) as redeemers +FROM redeemer + INNER JOIN TX ON tx.id = redeemer.tx_id + INNER JOIN REDEEMER_DATA rd on rd.id = redeemer.redeemer_data_id +WHERE redeemer.script_hash = _script_hash_bytea +GROUP BY redeemer.script_hash; +END; +$$; + +COMMENT ON FUNCTION grest.script_redeemers IS 'Get all redeemers for a given script hash.'; \ No newline at end of file diff --git a/files/grest/rpc/transactions/tx_info.sql b/files/grest/rpc/transactions/tx_info.sql new file mode 100644 index 00000000..ed106264 --- /dev/null +++ b/files/grest/rpc/transactions/tx_info.sql @@ -0,0 +1,828 @@ +CREATE FUNCTION grest.tx_info (_tx_hashes text[]) + RETURNS TABLE ( + tx_hash text, + block_hash text, + block_height word31type, + epoch_no word31type, + epoch_slot word31type, + absolute_slot word63type, + tx_timestamp integer, + tx_block_index word31type, + tx_size word31type, + total_output text, + fee text, + deposit text, + invalid_before word64type, + invalid_after word64type, + collateral_inputs jsonb, + collateral_output jsonb, + reference_inputs jsonb, + inputs jsonb, + outputs jsonb, + withdrawals jsonb, + assets_minted jsonb, + metadata jsonb, + certificates jsonb, + native_scripts jsonb, + plutus_contracts jsonb + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _tx_hashes_bytea bytea[]; + _tx_id_list bigint[]; +BEGIN + + -- convert input _tx_hashes array into bytea array + SELECT INTO _tx_hashes_bytea ARRAY_AGG(hashes_bytea) + FROM ( + SELECT + DECODE(hashes_hex, 'hex') AS hashes_bytea + FROM + UNNEST(_tx_hashes) AS hashes_hex + ) AS tmp; + + -- all tx ids + SELECT INTO _tx_id_list ARRAY_AGG(id) + FROM ( + SELECT + id + FROM + tx + WHERE tx.hash = ANY (_tx_hashes_bytea) + ) AS tmp; + + RETURN QUERY ( + WITH + + -- limit by last known block, also join with block only once + _all_tx AS ( + SELECT + tx.id, + tx.hash AS tx_hash, + b.hash AS block_hash, + b.block_no AS block_height, + b.epoch_no AS epoch_no, + b.epoch_slot_no AS epoch_slot, + b.slot_no AS absolute_slot, + b.time AS tx_timestamp, + tx.block_index AS tx_block_index, + tx.size AS tx_size, + tx.out_sum AS total_output, + tx.fee, + tx.deposit, + tx.invalid_before, + tx.invalid_hereafter AS invalid_after + FROM + tx + INNER JOIN block b ON tx.block_id = b.id + WHERE tx.id = ANY (_tx_id_list) + ), + + _all_collateral_inputs AS ( + SELECT + collateral_tx_in.tx_in_id AS tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + SA.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + ( CASE WHEN MA.policy IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTO.quantity::text + ) + END + ) AS asset_list, + ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END + ) AS inline_datum, + ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END + ) AS reference_script + FROM + collateral_tx_in + INNER JOIN tx_out ON tx_out.tx_id = collateral_tx_in.tx_out_id + AND tx_out.index = collateral_tx_in.tx_out_index + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id + LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id + LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.id = tx_out.reference_script_id + WHERE + collateral_tx_in.tx_in_id = ANY (_tx_id_list) + ), + + _all_reference_inputs AS ( + SELECT + reference_tx_in.tx_in_id AS tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + SA.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + ( CASE WHEN MA.policy IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTO.quantity::text + ) + END + ) AS asset_list, + ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END + ) AS inline_datum, + ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END + ) AS reference_script + FROM + reference_tx_in + INNER JOIN tx_out ON tx_out.tx_id = reference_tx_in.tx_out_id + AND tx_out.index = reference_tx_in.tx_out_index + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id + LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id + LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.id = tx_out.reference_script_id + WHERE + reference_tx_in.tx_in_id = ANY (_tx_id_list) + ), + + _all_inputs AS ( + SELECT + tx_in.tx_in_id AS tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + SA.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + ( CASE WHEN MA.policy IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTO.quantity::text + ) + END + ) AS asset_list, + ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END + ) AS inline_datum, + ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END + ) AS reference_script + FROM + tx_in + INNER JOIN tx_out ON tx_out.tx_id = tx_in.tx_out_id + AND tx_out.index = tx_in.tx_out_index + INNER JOIN tx on tx_out.tx_id = tx.id + LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id + LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id + LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.id = tx_out.reference_script_id + WHERE + tx_in.tx_in_id = ANY (_tx_id_list) + ), + + _all_collateral_outputs AS ( + SELECT + tx_out.tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + SA.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + ( CASE WHEN MA.policy IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTO.quantity::text + ) + END + ) AS asset_list, + ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END + ) AS inline_datum, + ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END + ) AS reference_script + FROM + collateral_tx_out tx_out + INNER JOIN tx on tx_out.tx_id = tx.id + LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id + LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id + LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.id = tx_out.reference_script_id + WHERE + tx_out.tx_id = ANY (_tx_id_list) + ), + + _all_outputs AS ( + SELECT + tx_out.tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + SA.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + ( CASE WHEN MA.policy IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTO.quantity::text + ) + END + ) AS asset_list, + ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END + ) AS inline_datum, + ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END + ) AS reference_script + FROM + tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id + LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id + LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.id = tx_out.reference_script_id + WHERE + tx_out.tx_id = ANY (_tx_id_list) + ), + + _all_withdrawals AS ( + SELECT + tx_id, + JSONB_AGG(data) AS list + FROM ( + SELECT + W.tx_id, + JSONB_BUILD_OBJECT( + 'amount', W.amount::text, + 'stake_addr', SA.view + ) AS data + FROM + withdrawal W + INNER JOIN stake_address SA ON W.addr_id = SA.id + WHERE + W.tx_id = ANY (_tx_id_list) + ) AS tmp + + GROUP BY tx_id + ), + + _all_mints AS ( + SELECT + tx_id, + JSONB_AGG(data) AS list + FROM ( + SELECT + MTM.tx_id, + JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTM.quantity::text + ) AS data + FROM + ma_tx_mint MTM + INNER JOIN MULTI_ASSET MA ON MA.id = MTM.ident + WHERE + MTM.tx_id = ANY (_tx_id_list) + ) AS tmp + + GROUP BY tx_id + ), + + _all_metadata AS ( + SELECT + tx_id, + JSONB_OBJECT_AGG( + tm.key::text, + tm.json + ) AS list + FROM + tx_metadata TM + WHERE + TM.tx_id = ANY (_tx_id_list) + GROUP BY tx_id + ), + + _all_certs AS ( + SELECT + tx_id, + JSONB_AGG(data) AS list + FROM ( + SELECT + SR.tx_id, + JSONB_BUILD_OBJECT( + 'index', SR.cert_index, + 'type', 'stake_registration', + 'info', JSONB_BUILD_OBJECT( + 'stake_address', SA.view + ) + ) AS data + FROM + public.stake_registration SR + INNER JOIN public.stake_address SA ON SA.id = SR.addr_id + WHERE + SR.tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + SD.tx_id, + JSONB_BUILD_OBJECT( + 'index', SD.cert_index, + 'type', 'stake_deregistration', + 'info', JSONB_BUILD_OBJECT( + 'stake_address', SA.view + ) + ) AS data + FROM + public.stake_deregistration SD + INNER JOIN public.stake_address SA ON SA.id = SD.addr_id + WHERE + SD.tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + D.tx_id, + JSONB_BUILD_OBJECT( + 'index', D.cert_index, + 'type', 'delegation', + 'info', JSONB_BUILD_OBJECT( + 'stake_address', SA.view, + 'pool_id_bech32', PH.view, + 'pool_id_hex', ENCODE(PH.hash_raw, 'hex') + ) + ) AS data + FROM + public.delegation D + INNER JOIN public.stake_address SA ON SA.id = D.addr_id + INNER JOIN public.pool_hash PH ON PH.id = D.pool_hash_id + WHERE + D.tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + T.tx_id, + JSONB_BUILD_OBJECT( + 'index', T.cert_index, + 'type', 'treasury_MIR', + 'info', JSONB_BUILD_OBJECT( + 'stake_address', SA.view, + 'amount', T.amount::text + ) + ) AS data + FROM + public.treasury T + INNER JOIN public.stake_address SA ON SA.id = T.addr_id + WHERE + T.tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + R.tx_id, + JSONB_BUILD_OBJECT( + 'index', R.cert_index, + 'type', 'reserve_MIR', + 'info', JSONB_BUILD_OBJECT( + 'stake_address', SA.view, + 'amount', R.amount::text + ) + ) AS data + FROM + public.reserve R + INNER JOIN public.stake_address SA ON SA.id = R.addr_id + WHERE + R.tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + PT.tx_id, + JSONB_BUILD_OBJECT( + 'index', PT.cert_index, + 'type', 'pot_transfer', + 'info', JSONB_BUILD_OBJECT( + 'treasury', PT.treasury::text, + 'reserves', PT.reserves::text + ) + ) AS data + FROM + public.pot_transfer PT + WHERE + PT.tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + -- SELECT DISTINCT below because there are multiple entries for each signing key of a given transaction + DISTINCT ON (PP.registered_tx_id) PP.registered_tx_id AS tx_id, + JSONB_BUILD_OBJECT( + 'index', null, -- cert_index not stored in param_proposal table + 'type', 'param_proposal', + 'info', JSONB_STRIP_NULLS(JSONB_BUILD_OBJECT( + 'min_fee_a', PP.min_fee_a, + 'min_fee_b', PP.min_fee_b, + 'max_block_size', PP.max_block_size, + 'max_tx_size', PP.max_tx_size, + 'max_bh_size', PP.max_bh_size, + 'key_deposit', PP.key_deposit, + 'pool_deposit', PP.pool_deposit, + 'max_epoch', PP.max_epoch, + 'optimal_pool_count', PP.optimal_pool_count, + 'influence', PP.influence, + 'monetary_expand_rate', PP.monetary_expand_rate, + 'treasury_growth_rate', PP.treasury_growth_rate, + 'decentralisation', PP.decentralisation, + 'entropy', PP.entropy, + 'protocol_major', PP.protocol_major, + 'protocol_minor', PP.protocol_minor, + 'min_utxo_value', PP.min_utxo_value, + 'min_pool_cost', PP.min_pool_cost, + 'cost_model', CM.costs, + 'price_mem', PP.price_mem, + 'price_step', PP.price_step, + 'max_tx_ex_mem', PP.max_tx_ex_mem, + 'max_tx_ex_steps', PP.max_tx_ex_steps, + 'max_block_ex_mem', PP.max_block_ex_mem, + 'max_block_ex_steps', PP.max_block_ex_steps, + 'max_val_size', PP.max_val_size, + 'collateral_percent', PP.collateral_percent, + 'max_collateral_inputs', PP.max_collateral_inputs, + 'coins_per_utxo_size', PP.coins_per_utxo_size + )) + ) AS data + FROM + public.param_proposal PP + INNER JOIN cost_model CM ON CM.id = PP.cost_model_id + WHERE + PP.registered_tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + PR.announced_tx_id AS tx_id, + JSONB_BUILD_OBJECT( + 'index', PR.cert_index, + 'type', 'pool_retire', + 'info', JSONB_BUILD_OBJECT( + 'pool_id_bech32', PH.view, + 'pool_id_hex', ENCODE(PH.hash_raw, 'hex'), + 'retiring epoch', PR.retiring_epoch + ) + ) AS data + FROM + public.pool_retire PR + INNER JOIN public.pool_hash PH ON PH.id = PR.hash_id + WHERE + PR.announced_tx_id = ANY (_tx_id_list) + -- + UNION ALL + -- + SELECT + PIC.tx_id, + JSONB_BUILD_OBJECT( + 'index', PU.cert_index, + 'type', 'pool_update', + 'info', JSONB_BUILD_OBJECT( + 'pool_id_bech32', PIC.pool_id_bech32, + 'pool_id_hex', PIC.pool_id_hex, + 'active_epoch_no', PIC.active_epoch_no, + 'vrf_key_hash', PIC.vrf_key_hash, + 'margin', PIC.margin, + 'fixed_cost', PIC.fixed_cost::text, + 'pledge', PIC.pledge::text, + 'reward_addr', PIC.reward_addr, + 'owners', PIC.owners, + 'relays', PIC.relays, + 'meta_url', PIC.meta_url, + 'meta_hash', PIC.meta_hash + ) + ) AS data + FROM + grest.pool_info_cache PIC + INNER JOIN public.pool_update PU ON PU.registered_tx_id = PIC.tx_id + WHERE + PIC.tx_id = ANY (_tx_id_list) + ) AS tmp + + GROUP BY tx_id + ), + + _all_native_scripts AS ( + SELECT + tx_id, + JSONB_AGG(data) AS list + FROM ( + SELECT + script.tx_id, + JSONB_BUILD_OBJECT( + 'script_hash', ENCODE(script.hash, 'hex'), + 'script_json', script.json + ) AS data + FROM + script + WHERE + script.tx_id = ANY (_tx_id_list) + AND + script.type = 'timelock' + ) AS tmp + + GROUP BY tx_id + ), + + _all_plutus_contracts AS ( + SELECT + tx_id, + JSONB_AGG(data) AS list + FROM ( + SELECT + redeemer.tx_id, + JSONB_BUILD_OBJECT( + 'address', INUTXO.address, + 'script_hash', ENCODE(script.hash, 'hex'), + 'bytecode', ENCODE(script.bytes, 'hex'), + 'size', script.serialised_size, + 'valid_contract', tx.valid_contract, + 'input', JSONB_BUILD_OBJECT( + 'redeemer', JSONB_BUILD_OBJECT( + 'purpose', redeemer.purpose, + 'fee', redeemer.fee::text, + 'unit', JSONB_BUILD_OBJECT( + 'steps', redeemer.unit_steps::text, + 'mem', redeemer.unit_mem::text + ), + 'datum', JSONB_BUILD_OBJECT( + 'hash', ENCODE(rd.hash, 'hex'), + 'value', rd.value + ) + ), + 'datum', JSONB_BUILD_OBJECT( + 'hash', ENCODE(ind.hash, 'hex'), + 'value', ind.value + ) + ), + 'output', CASE WHEN outd.hash IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(outd.hash, 'hex'), + 'value', outd.value + ) + END + ) AS data + FROM + redeemer + INNER JOIN tx ON redeemer.tx_id = tx.id + INNER JOIN redeemer_data RD ON RD.id = redeemer.redeemer_data_id + INNER JOIN script ON redeemer.script_hash = script.hash + INNER JOIN tx_in ON tx_in.redeemer_id = redeemer.id + INNER JOIN tx_out INUTXO ON INUTXO.tx_id = tx_in.tx_out_id AND INUTXO.index = tx_in.tx_out_index + INNER JOIN datum IND ON IND.hash = INUTXO.data_hash + LEFT JOIN tx_out OUTUTXO ON OUTUTXO.tx_id = redeemer.tx_id AND OUTUTXO.address = INUTXO.address + LEFT JOIN datum OUTD ON OUTD.hash = OUTUTXO.data_hash + WHERE + redeemer.tx_id = ANY (_tx_id_list) + ) AS tmp + + GROUP BY tx_id + ) + + SELECT + ENCODE(ATX.tx_hash, 'hex'), + ENCODE(ATX.block_hash, 'hex'), + ATX.block_height, + ATX.epoch_no, + ATX.epoch_slot, + ATX.absolute_slot, + EXTRACT(epoch from ATX.tx_timestamp)::integer, + ATX.tx_block_index, + ATX.tx_size, + ATX.total_output::text, + ATX.fee::text, + ATX.deposit::text, + ATX.invalid_before, + ATX.invalid_after, + COALESCE(( + SELECT JSONB_AGG(tx_collateral_inputs) + FROM ( + SELECT + JSONB_BUILD_OBJECT( + 'payment_addr', JSONB_BUILD_OBJECT( + 'bech32', payment_addr_bech32, + 'cred', payment_addr_cred + ), + 'stake_addr', stake_addr, + 'tx_hash', ACI.tx_hash, + 'tx_index', tx_index, + 'value', value, + 'datum_hash', datum_hash, + 'inline_datum', inline_datum, + 'reference_script', reference_script, + 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) + ) AS tx_collateral_inputs + FROM _all_collateral_inputs ACI + WHERE ACI.tx_id = ATX.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ACI.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + ) AS tmp + ), JSONB_BUILD_ARRAY()), + ( + SELECT + JSONB_BUILD_OBJECT( + 'payment_addr', JSONB_BUILD_OBJECT( + 'bech32', payment_addr_bech32, + 'cred', payment_addr_cred + ), + 'stake_addr', stake_addr, + 'tx_hash', ACO.tx_hash, + 'tx_index', tx_index, + 'value', value, + 'datum_hash', datum_hash, + 'inline_datum', inline_datum, + 'reference_script', reference_script, + 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) + ) AS tx_collateral_outputs + FROM _all_collateral_outputs ACO + WHERE ACO.tx_id = ATX.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ACO.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + LIMIT 1 -- there can only be one collateral output + ), + COALESCE(( + SELECT JSONB_AGG(tx_reference_inputs) + FROM ( + SELECT + JSONB_BUILD_OBJECT( + 'payment_addr', JSONB_BUILD_OBJECT( + 'bech32', payment_addr_bech32, + 'cred', payment_addr_cred + ), + 'stake_addr', stake_addr, + 'tx_hash', ARI.tx_hash, + 'tx_index', tx_index, + 'value', value, + 'datum_hash', datum_hash, + 'inline_datum', inline_datum, + 'reference_script', reference_script, + 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) + ) AS tx_reference_inputs + FROM _all_reference_inputs ARI + WHERE ARI.tx_id = ATX.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ARI.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + ) AS tmp + ), JSONB_BUILD_ARRAY()), + COALESCE(( + SELECT JSONB_AGG(tx_inputs) + FROM ( + SELECT + JSONB_BUILD_OBJECT( + 'payment_addr', JSONB_BUILD_OBJECT( + 'bech32', payment_addr_bech32, + 'cred', payment_addr_cred + ), + 'stake_addr', stake_addr, + 'tx_hash', AI.tx_hash, + 'tx_index', tx_index, + 'value', value, + 'datum_hash', datum_hash, + 'inline_datum', inline_datum, + 'reference_script', reference_script, + 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) + ) AS tx_inputs + FROM _all_inputs AI + WHERE AI.tx_id = ATX.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AI.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + ) AS tmp + ), JSONB_BUILD_ARRAY()), + COALESCE(( + SELECT JSONB_AGG(tx_outputs) + FROM ( + SELECT + JSONB_BUILD_OBJECT( + 'payment_addr', JSONB_BUILD_OBJECT( + 'bech32', payment_addr_bech32, + 'cred', payment_addr_cred + ), + 'stake_addr', stake_addr, + 'tx_hash', AO.tx_hash, + 'tx_index', tx_index, + 'value', value, + 'datum_hash', datum_hash, + 'inline_datum', inline_datum, + 'reference_script', reference_script, + 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) + ) AS tx_outputs + FROM _all_outputs AO + WHERE AO.tx_id = ATX.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AO.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + ) AS tmp + ), JSONB_BUILD_ARRAY()), + COALESCE((SELECT AW.list FROM _all_withdrawals AW WHERE AW.tx_id = ATX.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT AMI.list FROM _all_mints AMI WHERE AMI.tx_id = ATX.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT AME.list FROM _all_metadata AME WHERE AME.tx_id = ATX.id), NULL), + COALESCE((SELECT AC.list FROM _all_certs AC WHERE AC.tx_id = ATX.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT ANS.list FROM _all_native_scripts ANS WHERE ANS.tx_id = ATX.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT APC.list FROM _all_plutus_contracts APC WHERE APC.tx_id = ATX.id), JSONB_BUILD_ARRAY()) + FROM + _all_tx ATX + WHERE ATX.tx_hash = ANY (_tx_hashes_bytea) +); + +END; +$$; + +COMMENT ON FUNCTION grest.tx_info IS 'Get information about transactions.'; diff --git a/files/grest/rpc/transactions/tx_metadata.sql b/files/grest/rpc/transactions/tx_metadata.sql new file mode 100644 index 00000000..d4302614 --- /dev/null +++ b/files/grest/rpc/transactions/tx_metadata.sql @@ -0,0 +1,41 @@ +CREATE FUNCTION grest.tx_metadata (_tx_hashes text[]) + RETURNS TABLE ( + tx_hash text, + metadata jsonb) + LANGUAGE PLPGSQL + AS $$ +BEGIN + RETURN QUERY + SELECT + T1.tx_hash, + METADATA_T.metadata + FROM ( + SELECT + tx.id, + ENCODE(tx.hash, 'hex') as tx_hash + FROM + public.tx + WHERE + tx.hash::bytea = ANY ( + SELECT + DECODE(hashes, 'hex') + FROM + UNNEST(_tx_hashes) AS hashes + ) + ) T1 + LEFT JOIN LATERAL ( + SELECT + JSONB_OBJECT_AGG( + tx_metadata.key::text, + tx_metadata.json + ) as metadata + FROM + tx_metadata + WHERE + tx_id = T1.id + ) METADATA_T ON TRUE; +END; +$$; + +COMMENT ON FUNCTION grest.tx_metadata IS 'Get transaction metadata.'; + diff --git a/files/grest/rpc/transactions/tx_metalabels.sql b/files/grest/rpc/transactions/tx_metalabels.sql new file mode 100644 index 00000000..7b9b4c7f --- /dev/null +++ b/files/grest/rpc/transactions/tx_metalabels.sql @@ -0,0 +1,21 @@ +DROP FUNCTION IF EXISTS grest.tx_metalabels; + +CREATE FUNCTION grest.tx_metalabels() + RETURNS TABLE (key text) + LANGUAGE PLPGSQL + AS $$ +BEGIN + RETURN QUERY + WITH RECURSIVE t AS ( + (SELECT tm.key FROM public.tx_metadata tm ORDER BY key LIMIT 1) + UNION ALL + SELECT (SELECT tm.key FROM tx_metadata tm WHERE tm.key > t.key ORDER BY key LIMIT 1) + FROM t + WHERE t.key IS NOT NULL + ) + SELECT t.key::text FROM t WHERE t.key IS NOT NULL; +END; +$$; + +COMMENT ON FUNCTION grest.tx_metalabels IS 'Get a list of all transaction metalabels'; + diff --git a/files/grest/rpc/transactions/tx_status.sql b/files/grest/rpc/transactions/tx_status.sql new file mode 100644 index 00000000..617ebb18 --- /dev/null +++ b/files/grest/rpc/transactions/tx_status.sql @@ -0,0 +1,26 @@ +CREATE FUNCTION grest.tx_status (_tx_hashes text[]) + RETURNS TABLE ( + tx_hash text, + num_confirmations integer) + LANGUAGE plpgsql + AS $$ +DECLARE + _curr_block_no word31type; +BEGIN + SELECT + max(block_no) INTO _curr_block_no + FROM + block b; + + RETURN QUERY ( + select HASHES, (_curr_block_no - b.block_no) + from UNNEST(_tx_hashes) WITH ORDINALITY HASHES + left outer join tx t on t.hash = DECODE(HASHES, 'hex') + left outer join block b on t.block_id = b.id + ORDER BY ordinality + ); +END; +$$; + +COMMENT ON FUNCTION grest.tx_status IS 'Returns number of blocks that were created since the block containing a transactions with a given hash'; + diff --git a/files/grest/rpc/transactions/tx_utxos.sql b/files/grest/rpc/transactions/tx_utxos.sql new file mode 100644 index 00000000..6c3514ef --- /dev/null +++ b/files/grest/rpc/transactions/tx_utxos.sql @@ -0,0 +1,156 @@ +CREATE FUNCTION grest.tx_utxos (_tx_hashes text[]) + RETURNS TABLE ( + tx_hash text, + inputs json, + outputs json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _tx_hashes_bytea bytea[]; + _tx_id_list bigint[]; +BEGIN + -- convert input _tx_hashes array into bytea array + SELECT INTO _tx_hashes_bytea ARRAY_AGG(hashes_bytea) + FROM ( + SELECT + DECODE(hashes_hex, 'hex') AS hashes_bytea + FROM + UNNEST(_tx_hashes) AS hashes_hex + ) AS tmp; + + -- all tx ids + SELECT INTO _tx_id_list ARRAY_AGG(id) + FROM ( + SELECT + id + FROM + tx + WHERE tx.hash = ANY (_tx_hashes_bytea) + ) AS tmp; + + RETURN QUERY ( + WITH + + -- tx id / hash mapping + _all_tx AS ( + SELECT + tx.id AS tx_id, + tx.hash AS tx_hash + FROM + tx + WHERE tx.id = ANY (_tx_id_list) + ), + + _all_inputs AS ( + SELECT + tx_in.tx_in_id AS tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + SA.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ( CASE WHEN MA.policy IS NULL THEN NULL + ELSE + JSON_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTO.quantity::text + ) + END + ) AS asset_list + FROM + tx_in + INNER JOIN tx_out ON tx_out.tx_id = tx_in.tx_out_id + AND tx_out.index = tx_in.tx_out_index + INNER JOIN tx on tx_out.tx_id = tx.id + LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id + LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id + LEFT JOIN multi_asset MA ON MA.id = MTO.ident + WHERE + tx_in.tx_in_id = ANY (_tx_id_list) + ), + + _all_outputs AS ( + SELECT + tx_out.tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + SA.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ( CASE WHEN MA.policy IS NULL THEN NULL + ELSE + JSON_BUILD_OBJECT( + 'policy_id', ENCODE(MA.policy, 'hex'), + 'asset_name', ENCODE(MA.name, 'hex'), + 'fingerprint', MA.fingerprint, + 'quantity', MTO.quantity::text + ) + END + ) AS asset_list + FROM + tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id + LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id + LEFT JOIN multi_asset MA ON MA.id = MTO.ident + WHERE + tx_out.tx_id = ANY (_tx_id_list) + ) + + SELECT + ENCODE(ATX.tx_hash, 'hex'), + COALESCE(( + SELECT JSON_AGG(tx_inputs) + FROM ( + SELECT + JSON_BUILD_OBJECT( + 'payment_addr', JSON_BUILD_OBJECT( + 'bech32', payment_addr_bech32, + 'cred', payment_addr_cred + ), + 'stake_addr', stake_addr, + 'tx_hash', AI.tx_hash, + 'tx_index', tx_index, + 'value', value, + 'asset_list', COALESCE(JSON_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSON_BUILD_ARRAY()) + ) AS tx_inputs + FROM _all_inputs AI + WHERE AI.tx_id = ATX.tx_id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AI.tx_hash, tx_index, value + ) AS tmp + ), JSON_BUILD_ARRAY()), + COALESCE(( + SELECT JSON_AGG(tx_outputs) + FROM ( + SELECT + JSON_BUILD_OBJECT( + 'payment_addr', JSON_BUILD_OBJECT( + 'bech32', payment_addr_bech32, + 'cred', payment_addr_cred + ), + 'stake_addr', stake_addr, + 'tx_hash', AO.tx_hash, + 'tx_index', tx_index, + 'value', value, + 'asset_list', COALESCE(JSON_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSON_BUILD_ARRAY()) + ) AS tx_outputs + FROM _all_outputs AO + WHERE AO.tx_id = ATX.tx_id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AO.tx_hash, tx_index, value + ) AS tmp + ), JSON_BUILD_ARRAY()) + FROM + _all_tx ATX + WHERE ATX.tx_hash = ANY (_tx_hashes_bytea) +); + +END; +$$; + +COMMENT ON FUNCTION grest.tx_utxos IS 'Get UTXO set (inputs/outputs) of transactions.'; + diff --git a/files/grest/rpc/views/account_list.sql b/files/grest/rpc/views/account_list.sql new file mode 100644 index 00000000..2656a359 --- /dev/null +++ b/files/grest/rpc/views/account_list.sql @@ -0,0 +1,10 @@ +DROP VIEW IF EXISTS grest.account_list; + +CREATE VIEW grest.account_list AS + SELECT + STAKE_ADDRESS.VIEW AS ID + FROM + STAKE_ADDRESS; + +COMMENT ON VIEW grest.account_list IS 'Get a list of all accounts'; + diff --git a/files/grest/rpc/views/asset_list.sql b/files/grest/rpc/views/asset_list.sql new file mode 100644 index 00000000..b41ecaa7 --- /dev/null +++ b/files/grest/rpc/views/asset_list.sql @@ -0,0 +1,12 @@ +DROP VIEW IF EXISTS grest.asset_list; + +CREATE VIEW grest.asset_list AS + SELECT + ENCODE(MA.policy, 'hex') AS policy_id, + ENCODE(MA.name, 'hex') AS asset_name, + MA.fingerprint + FROM + public.multi_asset MA + ORDER BY MA.policy, MA.name; + +COMMENT ON VIEW grest.asset_list IS 'Get the list of all native assets'; diff --git a/files/grest/rpc/views/blocks.sql b/files/grest/rpc/views/blocks.sql new file mode 100644 index 00000000..6cc5d5c8 --- /dev/null +++ b/files/grest/rpc/views/blocks.sql @@ -0,0 +1,27 @@ +DROP VIEW IF EXISTS grest.blocks; + +CREATE VIEW grest.blocks AS + SELECT + ENCODE(B.HASH::bytea, 'hex') AS HASH, + b.EPOCH_NO AS EPOCH_NO, + b.SLOT_NO AS ABS_SLOT, + b.EPOCH_SLOT_NO AS EPOCH_SLOT, + b.BLOCK_NO AS BLOCK_HEIGHT, + b.SIZE AS BLOCK_SIZE, + EXTRACT(epoch from b.TIME)::integer AS BLOCK_TIME, + b.TX_COUNT, + b.VRF_KEY, + ph.VIEW AS POOL, + b.PROTO_MAJOR, + b.PROTO_MINOR, + b.OP_CERT_COUNTER + FROM + BLOCK B + LEFT JOIN SLOT_LEADER SL ON SL.ID = B.SLOT_LEADER_ID + LEFT JOIN POOL_HASH PH ON PH.ID = SL.POOL_HASH_ID + WHERE + B.BLOCK_NO IS NOT NULL + ORDER BY + B.ID DESC; + +COMMENT ON VIEW grest.blocks IS 'Get detailed information about all blocks (paginated - latest first)'; diff --git a/specs/createspecs.py b/specs/createspecs.py index a58f2711..4d32cd7b 100755 --- a/specs/createspecs.py +++ b/specs/createspecs.py @@ -29,6 +29,8 @@ def main(): populate_spec("m", "results/koiosapi-mainnet.yaml") populate_spec("t", "results/koiosapi-testnet.yaml") populate_spec("g", "results/koiosapi-guild.yaml") + populate_spec("pv", "results/koiosapi-preview.yaml") + populate_spec("pp", "results/koiosapi-preprod.yaml") print("Done!!") try: diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 48eaf8d6..c1bee3ac 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.8 + version: 1.0.9rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -666,6 +666,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses (accounts) + /account_info_cached: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (accounts) /account_rewards: #RPC post: tags: @@ -910,6 +931,7 @@ paths: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": description: Array of Tx hashes that included the given asset @@ -923,8 +945,8 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transaction History - description: Get the list of all asset transaction hashes (newest first) + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) /pool_list: #RPC get: tags: @@ -1288,7 +1310,7 @@ components: description: Block height for specifying time delta schema: type: integer - example: 63487 + example: 50000 in: query required: false allowEmptyValue: true @@ -1368,10 +1390,20 @@ components: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: 41484c636f696e + example: "41484c636f696e" in: query required: false allowEmptyValue: true + _history: + deprecated: false + name: _history + description: Include all historical transactions, setting to false includes only the non-empty ones + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: false _pool_bech32: deprecated: false name: _pool_bech32 @@ -2894,7 +2926,6 @@ components: } plutus_contracts: type: array - nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -2949,6 +2980,7 @@ components: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" output: type: object + nullable: true properties: hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" @@ -3148,7 +3180,10 @@ components: description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" metadata: - $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + type: array + description: Array of Transaction Metadata for given transaction + items: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index b8f9e182..1acf4877 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.8 + version: 1.0.9rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -666,6 +666,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses (accounts) + /account_info_cached: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (accounts) /account_rewards: #RPC post: tags: @@ -910,6 +931,7 @@ paths: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": description: Array of Tx hashes that included the given asset @@ -923,8 +945,8 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transaction History - description: Get the list of all asset transaction hashes (newest first) + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) /pool_list: #RPC get: tags: @@ -1288,7 +1310,7 @@ components: description: Block height for specifying time delta schema: type: integer - example: 63487 + example: 50000 in: query required: false allowEmptyValue: true @@ -1368,10 +1390,20 @@ components: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: 424f4f4b + example: "424f4f4b" in: query required: false allowEmptyValue: true + _history: + deprecated: false + name: _history + description: Include all historical transactions, setting to false includes only the non-empty ones + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: false _pool_bech32: deprecated: false name: _pool_bech32 @@ -1437,8 +1469,8 @@ components: description: Array of Cardano payment address(es) in bech32 format example: _addresses: - - addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g - - addr1qyfldpcvte8nkfpyv0jdc8e026cz5qedx7tajvupdu2724tlj8sypsq6p90hl40ya97xamkm9fwsppus2ru8zf6j8g9sm578cu + - addr1qy2jt0qpqz2z2z9zx5w4xemekkce7yderz53kjue53lpqv90lkfa9sgrfjuz6uvt4uqtrqhl2kj0a9lnr9ndzutx32gqleeckv + - addr1q9xvgr4ehvu5k5tmaly7ugpnvekpqvnxj8xy50pa7kyetlnhel389pa4rnq6fmkzwsaynmw0mnldhlmchn2sfd589fgsz9dd0y address_txs: content: application/json: @@ -1459,8 +1491,8 @@ components: description: Only fetch information after specific block height example: _addresses: - - addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g - - addr1qyfldpcvte8nkfpyv0jdc8e026cz5qedx7tajvupdu2724tlj8sypsq6p90hl40ya97xamkm9fwsppus2ru8zf6j8g9sm578cu + - addr1qy2jt0qpqz2z2z9zx5w4xemekkce7yderz53kjue53lpqv90lkfa9sgrfjuz6uvt4uqtrqhl2kj0a9lnr9ndzutx32gqleeckv + - addr1q9xvgr4ehvu5k5tmaly7ugpnvekpqvnxj8xy50pa7kyetlnhel389pa4rnq6fmkzwsaynmw0mnldhlmchn2sfd589fgsz9dd0y _after_block_height: 6238675 stake_addresses_with_epoch_no: content: @@ -2894,7 +2926,6 @@ components: } plutus_contracts: type: array - nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -2949,6 +2980,7 @@ components: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" output: type: object + nullable: true properties: hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" @@ -3148,7 +3180,10 @@ components: description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" metadata: - $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + type: array + description: Array of Transaction Metadata for given transaction + items: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml new file mode 100644 index 00000000..6ea78cab --- /dev/null +++ b/specs/results/koiosapi-preprod.yaml @@ -0,0 +1,3353 @@ +openapi: 3.0.2 +info: + title: Koios API + version: 1.0.9rc + description: | + Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. + + > Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). + + # Problems solved by Koios + - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) + to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is + free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be + health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. + - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be + consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute + or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that + pass the health-check for the endpoint. + - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For + such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that + take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part + of Koios API. + - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants + automatically receive failover support, they reduce impact of any subset of clusters going through update process. + - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to + give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. + - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will + ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. + - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. + + # Architecture + + ## How does Koios work? + + ![High-Level architecture overview](/koios-design.png) + + We will go bottom to top (from builder's eyes to run through the above) briefly: + + - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. + - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. + - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. + + # API Usage + + The endpoints served by Koios can be browsed from the left side bar of this site. You will find that almost each endpoint has an example that you can `Try` and will help you get an example in shell using cURL. For public queries, you do not need to register yourself - you can simply use them as per the examples provided on individual endpoints. But in addition, the [PostgREST API](https://postgrest.org/en/stable/api.html) used underneath provides a handful of features that can be quite handy for you to improve your queries to directly grab very specific information pertinent to your calls, reducing data you download and process. + + ## Vertical Filtering + + Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

+ + ``` bash + curl "https://api.koios.rest/api/v0/tip" + + # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] + + curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" + + # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] + ``` + + ## Horizontal Filtering + + You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

+ ``` bash + curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" + + # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, + # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] + ``` + + Here, we made use of `eq.` operator to denote a filter of "value equal to" against `epoch` column. Similarly, we added a filter using `lt.` operator to denote a filter of "values lower than" against `epoch_slot` column. You can find a complete list of operators supported in PostgREST documentation (commonly used ones extracted below): + + |Abbreviation|In PostgreSQL|Meaning | + |------------|-------------|-------------------------------------------| + |eq |`=` |equals | + |gt |`>` |greater than | + |gte |`>=` |greater than or equal | + |lt |`<` |less than | + |lte |`<=` |less than or equal | + |neq |`<>` or `!=` |not equal | + |like |`LIKE` |LIKE operator (use * in place of %) | + |in |`IN` |one of a list of values, e.g. `?a=in.("hi,there","yes,you")`| + |is |`IS` |checking for exact equality (null,true,false,unknown)| + |cs |`@>` |contains e.g. `?tags=cs.{example, new}` | + |cd |`<@` |contained in e.g. `?values=cd.{1,2,3}` | + |not |`NOT` |negates another operator | + |or |`OR` |logical `OR` operator | + |and |`AND` |logical `AND` operator | + + ## Pagination (offset/limit) + + When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. + + The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. + + Sounds confusing? Let's see this in practice, to hopefully make it easier. + Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range + + # content-range: 0-999/* + + ``` + + As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range + + # content-range: 1000-1499/* + + ``` + + There is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range + + # content-range: 1000-1499/* + + ``` + + The above methods for pagination are very useful to keep your queries light as well as process the output in smaller pages, making better use of your resources and respecting server timeouts for response times. + + ## Ordering + + You can set a sorting order for returned queries against specific column(s). + Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" + + # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"dc9496eae64294b46f07eb20499ae6dae4d81fdc67c63c354397db91bda1ee55","epoch":236,"abs_slot":16598058,"epoch_slot":9258,"block_height":5086962,"block_time":1608164349,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"6ebc7b734c513bc19290d96ca573a09cac9503c5a349dd9892b9ab43f917f9bd","epoch":236,"abs_slot":16601491,"epoch_slot":12691,"block_height":5087097,"block_time":1608167782,"tx_count":0,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"2eac97548829fc312858bc56a40f7ce3bf9b0ca27ee8530283ccebb3963de1c0","epoch":236,"abs_slot":16602308,"epoch_slot":13508,"block_height":5087136,"block_time":1608168599,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}] + ``` + + ## Response Formats + + You can get the results from the PostgREST endpoints in CSV or JSON formats. The default response format will always be JSON, but if you'd like to switch, you can do so by specifying header `'Accept: text/csv'` or `'Accept: application/json'`. + Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" + + # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, + # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, + # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] + + curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" + + # epoch,epoch_slot,block_time + # 318,28491,1643607582 + # 318,28479,1643607570 + # 318,28406,1643607497 + + ``` + + ## Limits + + While use of Koios is completely free and there are no registration requirements to the usage, the monitoring layer will only restrict spam requests that can potentially cause high amount of load to backends. The emphasis is on using list of objects first, and then [bulk where available] query specific objects to drill down where possible - which forms higher performance results to consumer as well as instance provider. Some basic protection against patterns that could cause unexpected resource spikes are protected as per below: + + - Burst Limit: A single IP can query an endpoint up to 100 times within 10 seconds (that's about 8.64 million requests within a day). The sleep time if a limit is crossed is minimal (60 seconds) for that IP - during which, the monitoring layer will return HTTP Status `429 - Too many requests`. + - Pagination/Limits: Any query results fetched will be paginated by 1000 records (you can reduce limit and or control pagination offsets on URL itself, see API > Pagination section for more details). + - Query timeout: If a query from server takes more than 30 seconds, it will return a HTTP Status of `504 - Gateway timeout`. This is because we would want to ensure you're using the queries optimally, and more often than not - it would indicate that particular endpoint is not optimised (or the network connectivity is not optimal between servers). + + Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. + + + # Community projects + + A big thank you to the following projects who are already starting to use Koios from early days: + + ## CLI + + - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) + + ## Libraries + + - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) + - [Go Client](https://github.com/cardano-community/koios-go-client) + - [Java Client](https://github.com/cardano-community/koios-java-client) + + ## Community Projects/Tools + + - [Building On Cardano](https://buildingoncardano.com) + - [CardaStat](cardastat.info) + - [CNFT.IO](https://cnft.io) + - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) + - [Dandelion](https://dandelion.link) + - [Eternl](https://eternl.io/) + - [PoolPeek](https://poolpeek.com) + - [TosiDrop](https://tosidrop.io) + + # FAQ + + ### Is there a price attached to using services? + For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). + + ### Who are the folks behind Koios? + It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) + who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many + for experiments. + + ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? + All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. + + ### I am not sure how to set up an instance. Is there an easy start guide? + Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). + + ### Too much reading, I want to discuss in person + There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. + + x-logo: + url: "https://api.koios.rest/koios.png" +servers: + - url: https://api.koios.rest/api/v0 + - url: https://guild.koios.rest/api/v0 + - url: https://testnet.koios.rest/api/v0 +paths: + /tip: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of block summary (limit+paginated) + content: + application/json: + schema: + $ref: "#/components/schemas/tip" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Query Chain Tip + description: Get the tip info about the latest block seen by chain + /genesis: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of genesis parameters used to start each era on chain + content: + application/json: + schema: + $ref: "#/components/schemas/genesis" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Get Genesis info + description: Get the Genesis parameters used to start specific era on chain + /totals: #RPC + get: + tags: + - Network + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of supply/reserves/utxo/fees/treasury stats + content: + application/json: + schema: + $ref: "#/components/schemas/totals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Get historical tokenomic stats + description: >- + Get the circulating utxo, treasury, rewards, supply and reserves in + lovelace for specified epoch, all epochs if empty + /epoch_info: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of detailed summary for each epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch Information + description: Get the epoch information, all epochs if no epoch specified + /epoch_params: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of protocol parameters for each epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_params" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Protocol Parameters + description: >- + Get the protocol parameters for specific epoch, returns information + about all epochs if no epoch specified + /epoch_block_protocols: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of distinct block protocol versions counts in epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_block_protocols" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Block Protocols + description: >- + Get the information about block protocol distribution in epoch + /blocks: + get: + tags: + - Block + responses: + "200": + description: Array of block information + content: + application/json: + schema: + $ref: "#/components/schemas/blocks" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Block List + description: Get summarised details about all blocks (paginated - latest first) + /block_info: #RPC + post: + tags: + - Block + requestBody: + $ref: "#/components/requestBodies/block_hashes" + responses: + "200": + description: Array of detailed block information + content: + application/json: + schema: + $ref: "#/components/schemas/block_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Block Information + description: Get detailed information about a specific block + /block_txs: #RPC + post: + tags: + - Block + requestBody: + $ref: "#/components/requestBodies/block_hashes" + responses: + "200": + description: Array of transactions hashes + content: + application/json: + schema: + $ref: "#/components/schemas/block_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Block Transactions + description: Get a list of all transactions included in provided blocks + /tx_info: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of detailed information about transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Information + description: Get detailed information about transaction(s) + /tx_utxos: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of inputs and outputs for given transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_utxos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction UTxOs + description: Get UTxO set (inputs/outputs) of transactions. + /tx_metadata: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of metadata information present in each of the transactions queried + content: + application/json: + schema: + $ref: "#/components/schemas/tx_metadata" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Metadata + description: Get metadata information (if any) for given transaction(s) + /tx_metalabels: #RPC + get: + tags: + - Transactions + responses: + "200": + description: Array of known metadata labels + content: + application/json: + schema: + $ref: "#/components/schemas/tx_metalabels" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Metadata Labels + description: Get a list of all transaction metalabels + /submittx: #submit-api + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/txbin" + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} is a raw binary serialized transaction on the file-system. + # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/v0/submittx + responses: + "202": + description: OK + content: + application/json: + schema: + description: The transaction id. + type: string + format: hex + minLength: 64 + maxLength: 64 + example: 92bcd06b25dfbd89b578d536b4d3b7dd269b7c2aa206ed518012cffe0444d67f + "400": + description: An error occured while submitting transaction. + summary: Submit Transaction + description: Submit an already serialized transaction to the network. + /tx_status: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of transaction confirmation counts + content: + application/json: + schema: + $ref: "#/components/schemas/tx_status" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Status (Block Confirmations) + description: Get the number of block confirmations for a given transaction hash list + /address_info: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address information + content: + application/json: + schema: + $ref: "#/components/schemas/address_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Information + description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses + /address_txs: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/address_txs" + responses: + "200": + description: Array of transaction hashes + content: + application/json: + schema: + $ref: "#/components/schemas/address_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Transactions + description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) + /address_assets: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address-owned assets + content: + application/json: + schema: + $ref: "#/components/schemas/address_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Assets + description: Get the list of all the assets (policy, name and quantity) for given addresses + /credential_txs: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/credential_txs" + responses: + "200": + description: Array of transaction hashes + content: + application/json: + schema: + $ref: "#/components/schemas/credential_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transactions from payment credentials + description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) + /account_list: + get: + tags: + - Account + responses: + "200": + description: Array of account (stake address) IDs + content: + application/json: + schema: + $ref: "#/components/schemas/account_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account List + description: Get a list of all accounts + /account_info: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information + description: Get the account information for given stake addresses (accounts) + /account_info_cached: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (accounts) + /account_rewards: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + responses: + "200": + description: Array of reward history information + content: + application/json: + schema: + $ref: "#/components/schemas/account_rewards" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Rewards + description: >- + Get the full rewards history (including MIR) for given stake addresses (accounts) + /account_updates: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account updates information + content: + application/json: + schema: + $ref: "#/components/schemas/account_updates" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Updates + description: >- + Get the account updates (registration, deregistration, delegation and + withdrawals) for given stake addresses (accounts) + /account_addresses: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of payment addresses + content: + application/json: + schema: + $ref: "#/components/schemas/account_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Addresses + description: Get all addresses associated with given staking accounts + /account_assets: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of assets owned by account + content: + application/json: + schema: + $ref: "#/components/schemas/account_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Assets + description: Get the native asset balance of given accounts + /account_history: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + responses: + "200": + description: Array of active stake values per epoch + content: + application/json: + schema: + $ref: "#/components/schemas/account_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account History + description: Get the staking history of given stake addresses (accounts) + /asset_list: + get: + tags: + - Asset + responses: + "200": + description: Array of policy IDs and asset names + content: + application/json: + schema: + $ref: "#/components/schemas/asset_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset List + description: Get the list of all native assets (paginated) + /asset_address_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of payment addresses holding the given token (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/asset_address_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Address List + description: Get the list of all addresses holding a given asset + /asset_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information + description: Get the information of an asset including first minting & token registry metadata + /asset_history: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of asset mint/burn history + content: + application/json: + schema: + $ref: "#/components/schemas/asset_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset History + description: Get the mint/burn history of an asset + /asset_policy_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/asset_policy_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Policy Information + description: Get the information for all assets under the same policy + /asset_summary: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of asset summary information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_summary" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Summary + description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) + /asset_txs: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" + responses: + "200": + description: Array of Tx hashes that included the given asset + content: + application/json: + schema: + $ref: "#/components/schemas/asset_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) + /pool_list: #RPC + get: + tags: + - Pool + responses: + "200": + description: Array of pool IDs and tickers + content: + application/json: + schema: + $ref: "#/components/schemas/pool_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool List + description: A list of all currently registered/retiring (not retired) pools + /pool_info: #RPC + post: + tags: + - Pool + requestBody: + $ref: "#/components/requestBodies/pool_ids" + responses: + "200": + description: Array of pool information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Information + description: Current pool statuses and details for a specified list of pool ids + /pool_stake_snapshot: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool stake information for 3 snapshots + content: + application/json: + schema: + $ref: "#/components/schemas/pool_snapshot" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake Snapshot + description: Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation + /pool_delegators: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators List + description: Return information about live delegators for a given pool. + /pool_delegators_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators History + description: Return information about active delegators (incl. history) for a given pool and epoch number (all epochs if not specified). + /pool_blocks: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of blocks created by pool + content: + application/json: + schema: + $ref: "#/components/schemas/pool_blocks" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Blocks + description: >- + Return information about blocks minted by a given pool for all epochs + (or _epoch_no if provided) + /pool_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool history information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_history_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake, Block and Reward History + description: >- + Return information about pool stake, block and reward history in a given epoch _epoch_no + (or all epochs that pool existed for, in descending order if no _epoch_no was provided) + /pool_updates: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32_optional" + required: false + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_updates" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Updates (History) + description: Return all pool updates for all pools or only updates for specific pool if specified + /pool_relays: #RPC + get: + tags: + - Pool + responses: + "200": + description: Array of pool relay information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_relays" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Relays + description: A list of registered relays for all currently registered/retiring (not retired) pools + /pool_metadata: #RPC + post: + tags: + - Pool + requestBody: + $ref: "#/components/requestBodies/pool_ids_optional" + responses: + "200": + description: Array of pool metadata + content: + application/json: + schema: + $ref: "#/components/schemas/pool_metadata" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Metadata + description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools + /native_script_list: #RPC + get: + tags: + - Script + responses: + "200": + description: List of native script and creation tx hash pairs + content: + application/json: + schema: + $ref: "#/components/schemas/native_script_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Native Script List + description: List of all existing native script hashes along with their creation transaction hashes + /plutus_script_list: #RPC + get: + tags: + - Script + responses: + "200": + description: List of Plutus script and creation tx hash pairs + content: + application/json: + schema: + $ref: "#/components/schemas/plutus_script_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Plutus Script List + description: List of all existing Plutus script hashes along with their creation transaction hashes + /script_redeemers: #RPC + get: + tags: + - Script + parameters: + - $ref: "#/components/parameters/_script_hash" + responses: + "200": + description: List of all redeemers for a given script hash + content: + application/json: + schema: + $ref: "#/components/schemas/script_redeemers" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Script Redeemers + description: List of all redeemers for a given script hash + /datum_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/datum_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/datum_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes +components: + parameters: + select: + name: select + description: Filtering Columns + schema: + type: string + in: query + required: false + on_conflict: + name: on_conflict + description: On Conflict + schema: + type: string + in: query + required: false + order: + name: order + description: Ordering + schema: + type: string + in: query + required: false + range: + name: Range + description: Limiting and Pagination + schema: + type: string + in: header + required: false + rangeUnit: + name: Range-Unit + description: Limiting and Pagination + schema: + default: items + type: string + in: header + required: false + offset: + name: offset + description: Limiting and Pagination + schema: + type: string + in: query + required: false + limit: + name: limit + description: Limiting and Pagination + schema: + type: string + in: query + required: false + example: 10 + _block_hash: + deprecated: false + name: _block_hash + description: Block Hash in hex format + schema: + type: string + example: 021486aaa71f3c20c9fb26864fc8b00f7966a9007ff21e0753f16b9532450aa8 + in: query + required: true + allowEmptyValue: false + _after_block_height: + deprecated: false + name: _after_block_height + description: Block height for specifying time delta + schema: + type: integer + example: 50000 + in: query + required: false + allowEmptyValue: true + _epoch_no: + deprecated: false + name: _epoch_no + description: Epoch Number to fetch details for + schema: + type: string + example: 31 + in: query + required: false + allowEmptyValue: true + _earned_epoch_no: + deprecated: false + name: _epoch_no + description: Filter for earned rewards Epoch Number + schema: + type: string + example: 23 + in: query + required: false + allowEmptyValue: true + _any_address: + deprecated: false + name: _address + description: Cardano payment or staking address in bech32 format + schema: + type: string + example: stake_test1uqc6cmh4um4t7x9p0nh5chlmf4txwhpw7572u8utuvgg80g47wc6x + in: query + required: true + allowEmptyValue: false + _address: + deprecated: false + name: _address + description: Cardano payment address in bech32 format + schema: + type: string + example: addr_test1qr33ynuc6yg4w56mccww3kcwqn54el84225xhvgkukf6wm32ah6gtt8ca4g0jdjcw50fk5ng5kpuc5uz4qula3gdlywssd3nq3 + in: query + required: true + allowEmptyValue: false + _address_assets: + deprecated: false + name: _address + description: Cardano payment address in bech32 format + schema: + type: string + example: addr_test1qps7qvkjm6dxa6pnd65surma3qkpng02cc0xfyh6zjksge343f8yzpwq3av7gauswp5ec7nj2e5fxve0nptaknn5904s7zmvuh + in: query + required: true + allowEmptyValue: false + _stake_address: + deprecated: false + name: _stake_address + description: Cardano staking address (reward account) in bech32 format + schema: + type: string + example: stake_test1urkzeud48zxwnjc54emzmmc3gkg2r6d6tm2sd799jxjnqxqlfzmvk + in: query + required: true + allowEmptyValue: false + _asset_policy: + deprecated: false + name: _asset_policy + description: Asset Policy ID in hexadecimal format (hex) + schema: + type: string + example: c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e + in: query + required: true + allowEmptyValue: false + _asset_name: + deprecated: false + name: _asset_name + description: Asset Name in hexadecimal format (hex), empty asset name returns royalties + schema: + type: string + example: "7447454e53" + in: query + required: false + allowEmptyValue: true + _history: + deprecated: false + name: _history + description: Include all historical transactions, setting to false includes only the non-empty ones + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: false + _pool_bech32: + deprecated: false + name: _pool_bech32 + description: Pool ID in bech32 format + schema: + type: string + example: pool1tc3suyyxh9796rd92744mne82chxpmfc6mu7qgqzlx56sjfwvvl + in: query + required: true + allowEmptyValue: false + _pool_bech32_optional: + deprecated: false + name: _pool_bech32 + description: Pool ID in bech32 format (optional) + schema: + type: string + example: pool1tc3suyyxh9796rd92744mne82chxpmfc6mu7qgqzlx56sjfwvvl + in: query + required: false + allowEmptyValue: true + _script_hash: + deprecated: false + name: _script_hash + description: Script hash in hexadecimal format (hex) + schema: + type: string + example: 590555d7b5760e98ae2bdd29b356247776251dfab0a207bfce98a485 + in: query + required: true + allowEmptyValue: false + requestBodies: + block_hashes: + content: + application/json: + schema: + required: + - _block_hashes + type: object + properties: + _block_hashes: + format: text + type: array + items: + $ref: "#/components/schemas/blocks/items/properties/hash" + example: + _block_hashes: + - 2abeb8d1c1227139763be30ddb7a2fd79abd7d44195fca87a7c836a510b2802d + - 4e790b758c495953bb33c4aad4a4b4c1b98f7c2ec135ebd3db21f32059481718 + - 389da613316d2aec61edc34d51f1b3d004891ab38c9419771e5e0a3b12de3ef6 + payment_addresses: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + example: + _addresses: + - addr_test1vzpwq95z3xyum8vqndgdd9mdnmafh3djcxnc6jemlgdmswcve6tkw + - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc + address_txs: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + _after_block_height: + format: integer + type: integer + description: Only fetch information after specific block height + example: + _addresses: + - addr_test1vzpwq95z3xyum8vqndgdd9mdnmafh3djcxnc6jemlgdmswcve6tkw + - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc + _after_block_height: 9417 + stake_addresses_with_epoch_no: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _epoch_no: + format: integer + type: integer + description: Only fetch information for a specific epoch + example: + _stake_addresses: + - stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l + - stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx + _epoch_no: 30 + stake_addresses: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + example: + _stake_addresses: + - stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l + - stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx + credential_txs: + content: + application/json: + schema: + required: + - _payment_credentials + type: object + properties: + _payment_credentials: + format: text + type: array + items: + type: string + description: Array of Cardano payment credential(s) in hex format + _after_block_height: + format: integer + type: integer + description: Only fetch information after specific block height + example: + _payment_credentials: + - b429738bd6cc58b5c7932d001aa2bd05cfea47020a556c8c753d4436 + - 82e016828989cd9d809b50d6976d9efa9bc5b2c1a78d4b3bfa1bb83b + _after_block_height: 9417 + tx_ids: + content: + application/json: + schema: + required: + - _tx_hashes + type: object + properties: + _tx_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano Transaction hashes + example: + _tx_hashes: + - d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530 + - 145688d3619e7524510ea64c0ec6363b77a9b8da179ef9bb0273a0940d57d576 + txbin: + content: + application/cbor: + schema: + type: string + format: binary + example: d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530 + pool_ids: + content: + application/json: + schema: + required: + - _pool_bech32_ids + type: object + properties: + _pool_bech32_ids: + format: text + type: array + items: + type: string + description: Array of Cardano pool IDs (bech32 format) + example: + _pool_bech32_ids: + - pool1ext7qrwjzaxcdfhdnkq5mth59ukuu2atcg6tgqpmevpt7ratkta + - pool1x4p3cwemsm356vpxnjwuud7w76jz64hyss729zp7xa6wuey6yr9 + - pool1ws42l6rawqjv58crs5l32v0eem3qnngpnjfd7epwd4lmjccc5cg + pool_ids_optional: + content: + application/json: + schema: + type: object + properties: + _pool_bech32_ids: + format: text + type: array + items: + type: string + description: Array of Cardano pool IDs (bech32 format) + example: + _pool_bech32_ids: + - pool1ext7qrwjzaxcdfhdnkq5mth59ukuu2atcg6tgqpmevpt7ratkta + - pool1x4p3cwemsm356vpxnjwuud7w76jz64hyss729zp7xa6wuey6yr9 + - pool1ws42l6rawqjv58crs5l32v0eem3qnngpnjfd7epwd4lmjccc5cg + datum_hashes: + content: + application/json: + schema: + type: object + properties: + _datum_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano datum hashes + example: + _datum_hashes: + - 5571e2c3549f15934a38382d1318707a86751fb70827f4cbd29b104480f1be9b + - 5f7212f546d7e7308ce99b925f05538db19981f4ea3084559c0b28a363245826 + securitySchemes: {} + schemas: + tip: + type: array + items: + properties: + hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + abs_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + block_no: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + genesis: + type: array + items: + properties: + networkmagic: + type: string + example: 764824073 + description: Unique network identifier for chain + networkid: + type: string + example: Mainnet + description: Network ID used at various CLI identification to distinguish between Mainnet and other networks + epochlength: + type: string + example: 432000 + description: Number of slots in an epoch + slotlength: + type: string + example: 1 + description: Duration of a single slot (in seconds) + maxlovelacesupply: + type: string + example: 45000000000000000 + description: Maximum smallest units (lovelaces) supply for the blockchain + systemstart: + type: integer + description: UNIX timestamp of the first block (genesis) on chain + example: 1506203091 + activeslotcoeff: + type: string + example: 0.05 + description: "Active Slot Co-Efficient (f) - determines the _probability_ of number of slots in epoch that are expected to have blocks (so mainnet, this would be: 432000 * 0.05 = 21600 estimated blocks)" + slotsperkesperiod: + type: string + example: 129600 + description: Number of slots that represent a single KES period (a unit used for validation of KES key evolutions) + maxkesrevolutions: + type: string + example: 62 + description: Number of KES key evolutions that will automatically occur before a KES (hot) key is expired. This parameter is for security of a pool, in case an operator had access to his hot(online) machine compromised + securityparam: + type: string + example: 2160 + description: A unit (k) used to divide epochs to determine stability window (used in security checks like ensuring atleast 1 block was created in 3*k/f period, or to finalize next epoch's nonce at 4*k/f slots before end of epoch) + updatequorum: + type: string + example: 5 + description: Number of BFT members that need to approve (via vote) a Protocol Update Proposal + alonzogenesis: + type: string + example: '{\"lovelacePerUTxOWord\":34482,\"executionPrices\":{\"prSteps\":{\"numerator\":721,\"denominator\":10000000},...' + description: A JSON dump of Alonzo Genesis + totals: + type: array + items: + properties: + epoch_no: + type: integer + description: Epoch number + example: 294 + circulation: + type: string + description: Circulating UTxOs for given epoch (in lovelaces) + example: 32081169442642320 + treasury: + type: string + description: Funds in treasury for given epoch (in lovelaces) + example: 637024173474141 + reward: + type: string + description: Rewards accumulated as of given epoch (in lovelaces) + example: 506871250479840 + supply: + type: string + description: Total Active Supply (sum of treasury funds, rewards, UTxOs, deposits and fees) for given epoch (in lovelaces) + example: 33228495612391330 + reserves: + type: string + description: Total Reserves yet to be unlocked on chain + example: 11771504387608670 + pool_list: + type: array + items: + properties: + pool_id_bech32: + type: string + nullable: true + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + ticker: + type: string + nullable: true + description: Pool ticker + example: JAZZ + pool_history_info: + type: array + items: + type: object + properties: + epoch_no: + type: integer + description: Epoch for which the pool history data is shown + example: 312 + active_stake: + type: string + description: Amount of delegated stake to this pool at the time of epoch snapshot (in lovelaces) + example: "31235800000" + active_stake_pct: + type: number + description: Active stake for the pool, expressed as a percentage of total active stake on network + example: 13.512182543475783 + saturation_pct: + type: number + description: Saturation percentage of a pool at the time of snapshot (2 decimals) + example: 45.32 + block_cnt: + type: integer + nullable: true + description: Number of blocks pool created in that epoch + example: 14 + delegator_cnt: + type: integer + description: Number of delegators to the pool for that epoch snapshot + example: 1432 + margin: + type: number + description: Margin (decimal format) + example: 0.125 + fixed_cost: + type: string + description: Pool fixed cost per epoch (in lovelaces) + example: "340000000" + pool_fees: + type: string + description: Total amount of fees earned by pool owners in that epoch (in lovelaces) + example: "123327382" + deleg_rewards: + type: string + description: Total amount of rewards earned by delegators in that epoch (in lovelaces) + example: "123456789123" + epoch_ros: + type: number + description: Annualized ROS (return on staking) for delegators for this epoch + example: 3.000340466 + pool_info: + type: array + items: + type: object + properties: + pool_id_bech32: + type: string + description: Pool ID (bech32 format) + example: pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc + pool_id_hex: + type: string + description: Pool ID (Hex format) + example: a532904ca60e13e88437b58e7c6ff66b8d5e7ec8d3f4b9e4be7820ec + active_epoch_no: + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" + vrf_key_hash: + type: string + description: Pool VRF key hash + example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 + margin: + type: number + description: Margin (decimal format) + example: 0.1 + fixed_cost: + type: string + description: Pool fixed cost per epoch + example: "500000000" + pledge: + type: string + description: Pool pledge in lovelace + example: "64000000000000" + reward_addr: + type: string + description: Pool reward address + example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 + owners: + type: array + items: + type: string + description: Pool (co)owner address + example: stake1u8088wvudd7dp3rxl0v9xgng8r3j50s65ge3l3jvgd94keqfm3nv3 + relays: + type: array + items: + type: object + properties: + dns: + type: string + nullable: true + description: DNS name of the relay (nullable) + example: relays-new.cardano-mainnet.iohk.io + srv: + type: string + nullable: true + description: DNS service name of the relay (nullable) + example: biostakingpool3.hopto.org + ipv4: + type: string + nullable: true + description: IPv4 address of the relay (nullable) + example: "54.220.20.40" + ipv6: + type: string + nullable: true + description: IPv6 address of the relay (nullable) + example: 2604:ed40:1000:1711:6082:78ff:fe0c:ebf + port: + type: number + nullable: true + description: Port number of the relay (nullable) + example: 6000 + meta_url: + type: string + description: Pool metadata URL + nullable: true + example: https://pools.iohk.io/IOGP.json + meta_hash: + type: string + description: Pool metadata hash + nullable: true + example: 37eb004c0dd8a221ac3598ca1c6d6257fb5207ae9857b7c163ae0f39259d6cc0 + meta_json: + type: object + nullable: true + properties: + name: + type: string + description: Pool name + example: Input Output Global (IOHK) - Private + ticker: + type: string + description: Pool ticker + example: IOGP + homepage: + type: string + description: Pool homepage URL + example: https://iohk.io + description: + type: string + description: Pool description + example: Our mission is to provide economic identity to the billions of people who lack it. IOHK will not use the IOHK ticker. + pool_status: + type: string + description: Pool status + enum: ["registered", "retiring", "retired"] + example: registered + retiring_epoch: + type: integer + description: Announced retiring epoch (nullable) + example: null + nullable: true + op_cert: + type: string + nullable: true + description: Pool latest operational certificate hash + example: 37eb004c0dd8a221ac3598ca1c6d6257fb5207ae9857b7c163ae0f39259d6cc0 + op_cert_counter: + type: integer + nullable: true + description: Pool latest operational certificate counter value + example: 8 + active_stake: + type: string + nullable: true + description: Pool active stake (will be null post epoch transition until dbsync calculation is complete) + example: "64328627680963" + sigma: + type: number + nullable: true + description: Pool relative active stake share + example: 0.0034839235 + block_count: + type: integer + nullable: true + description: Total pool blocks on chain + example: 4509 + live_pledge: + type: string + nullable: true + description: Summary of account balance for all pool owner's + example: "64328594406327" + live_stake: + type: string + nullable: true + description: Pool live stake + example: "64328627680963" + live_delegators: + type: integer + description: Pool live delegator count + example: 5 + live_saturation: + type: number + nullable: true + description: Pool live saturation (decimal format) + example: 94.52 + pool_snapshot: + type: array + nullable: true + items: + type: object + properties: + snapshot: + type: string + description: Type of snapshot ("Mark", "Set" or "Go") + example: "Mark" + epoch_no: + type: integer + description: Epoch number for the snapshot entry + example: 324 + nonce: + $ref: "#/components/schemas/epoch_params/items/properties/nonce" + pool_stake: + type: string + description: Pool's Active Stake for the given epoch + example: "100000000000" + active_stake: + type: string + description: Total Active Stake for the given epoch + example: "103703246364020" + pool_delegators: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + type: string + description: Current delegator live stake (in lovelace) + example: 64328591517480 + active_epoch_no: + type: integer + description: Epoch number in which the delegation becomes active + example: 324 + latest_delegation_hash: + type: string + description: Latest transaction hash used for delegation by the account + example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_delegators_history: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + epoch_no: + type: integer + description: Epoch number for the delegation history + example: 324 + pool_blocks: + type: array + nullable: true + items: + type: object + properties: + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + abs_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + pool_updates: + type: array + items: + type: object + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + pool_id_hex: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" + active_epoch_no: + type: integer + description: Epoch number in which the update becomes active + example: 324 + vrf_key_hash: + $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" + margin: + $ref: "#/components/schemas/pool_info/items/properties/margin" + fixed_cost: + $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" + pledge: + $ref: "#/components/schemas/pool_info/items/properties/pledge" + reward_addr: + $ref: "#/components/schemas/pool_info/items/properties/reward_addr" + owners: + $ref: "#/components/schemas/pool_info/items/properties/owners" + relays: + $ref: "#/components/schemas/pool_info/items/properties/relays" + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/meta_url" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" + retiring_epoch: + $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" + pool_relays: + type: array + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + relays: + $ref: "#/components/schemas/pool_info/items/properties/relays" + pool_metadata: + type: array + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/meta_url" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" + epoch_info: + type: array + items: + type: object + properties: + epoch_no: + type: integer + description: Epoch number + example: 294 + out_sum: + type: string + description: Total output value across all transactions in epoch + example: 15432725054364942 + fees: + type: string + description: Total fees incurred by transactions in epoch + example: 74325855210 + tx_count: + type: integer + description: Number of transactions submitted in epoch + example: 357919 + blk_count: + type: integer + description: Number of blocks created in epoch + example: 17321 + start_time: + type: integer + description: UNIX timestamp of the epoch start + example: 1506203091 + end_time: + type: integer + description: UNIX timestamp of the epoch end + example: 1506635091 + first_block_time: + type: integer + description: UNIX timestamp of the epoch's first block + example: 1506635091 + last_block_time: + type: integer + description: UNIX timestamp of the epoch's last block + example: 1506635091 + active_stake: + type: string + description: Total active stake in epoch stake snapshot (null for pre-Shelley epochs) + example: 23395112387185880 + nullable: true + total_rewards: + type: string + description: Total rewards earned in epoch (null for pre-Shelley epochs) + example: 252902897534230 + nullable: true + avg_blk_reward: + type: string + description: Average block reward for epoch (null for pre-Shelley epochs) + example: 660233450 + nullable: true + epoch_params: + type: array + items: + properties: + epoch_no: + type: integer + description: Epoch number + example: 294 + min_fee_a: + type: integer + description: The 'a' parameter to calculate the minimum transaction fee + example: 44 + nullable: true + min_fee_b: + type: integer + description: The 'b' parameter to calculate the minimum transaction fee + example: 155381 + nullable: true + max_block_size: + type: integer + description: The maximum block size (in bytes) + example: 65536 + nullable: true + max_tx_size: + type: integer + description: The maximum transaction size (in bytes) + example: 16384 + nullable: true + max_bh_size: + type: integer + description: The maximum block header size (in bytes) + example: 1100 + nullable: true + key_deposit: + type: string + description: The amount (in lovelace) required for a deposit to register a stake address + example: 2000000 + nullable: true + pool_deposit: + type: string + description: The amount (in lovelace) required for a deposit to register a stake pool + example: 500000000 + nullable: true + max_epoch: + type: integer + description: The maximum number of epochs in the future that a pool retirement is allowed to be scheduled for + example: 18 + nullable: true + optimal_pool_count: + type: integer + description: The optimal number of stake pools + example: 500 + nullable: true + influence: + type: number + format: double + description: The pledge influence on pool rewards + example: 0.3 + nullable: true + monetary_expand_rate: + type: number + format: double + description: The monetary expansion rate + example: 0.003 + nullable: true + treasury_growth_rate: + type: number + format: double + description: The treasury growth rate + example: 0.2 + nullable: true + decentralisation: + type: number + format: double + description: The decentralisation parameter (1 fully centralised, 0 fully decentralised) + example: 0.1 + nullable: true + extra_entropy: + type: string + description: The hash of 32-byte string of extra random-ness added into the protocol's entropy pool + example: d982e06fd33e7440b43cefad529b7ecafbaa255e38178ad4189a37e4ce9bf1fa + nullable: true + protocol_major: + type: integer + description: The protocol major version + example: 5 + nullable: true + protocol_minor: + type: integer + description: The protocol minor version + example: 0 + nullable: true + min_utxo_value: + type: string + description: The minimum value of a UTxO entry + example: 34482 + nullable: true + min_pool_cost: + type: string + description: The minimum pool cost + example: 340000000 + nullable: true + nonce: + type: string + description: The nonce value for this epoch + example: 01304ddf5613166be96fce27be110747f2c8fcb38776618ee79225ccb59b81e2 + nullable: true + block_hash: + type: string + description: The hash of the first block where these parameters are valid + example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 + cost_models: + type: string + description: The per language cost models + example: null + nullable: true + price_mem: + type: number + format: double + description: The per word cost of script memory usage + example: 0.0577 + nullable: true + price_step: + type: number + format: double + description: The cost of script execution step usage + example: 7.21e-05 + nullable: true + max_tx_ex_mem: + type: number + description: The maximum number of execution memory allowed to be used in a single transaction + example: 10000000 + nullable: true + max_tx_ex_steps: + type: number + description: The maximum number of execution steps allowed to be used in a single transaction + example: 10000000000 + nullable: true + max_block_ex_mem: + type: number + description: The maximum number of execution memory allowed to be used in a single block + example: 50000000 + nullable: true + max_block_ex_steps: + type: number + description: The maximum number of execution steps allowed to be used in a single block + example: 40000000000 + nullable: true + max_val_size: + type: number + description: The maximum Val size + example: 5000 + nullable: true + collateral_percent: + type: integer + description: The percentage of the tx fee which must be provided as collateral when including non-native scripts + example: 150 + nullable: true + max_collateral_inputs: + type: integer + description: The maximum number of collateral inputs allowed in a transaction + example: 3 + nullable: true + coins_per_utxo_size: + type: string + description: The cost per UTxO size + example: 34482 + nullable: true + epoch_block_protocols: + type: array + items: + properties: + proto_major: + type: integer + description: Protocol major version + example: 6 + proto_minor: + type: integer + description: Protocol major version + example: 2 + blocks: + type: integer + description: Amount of blocks with specified major and protocol combination + example: 2183 + blocks: + type: array + items: + type: object + properties: + hash: + type: string + description: Hash of the block + example: e8c6992d52cd74b577b79251e0351be25070797a0dbc486b2c284d0bf7aeea9c + epoch_no: + type: integer + description: Epoch number of the block + example: 321 + abs_slot: + type: integer + description: Absolute slot number of the block + example: 53384242 + epoch_slot: + type: integer + description: Slot number of the block in epoch + example: 75442 + block_height: + type: integer + description: Block height + example: 42325043 + block_size: + type: integer + description: Block size in bytes + example: 79109 + block_time: + type: integer + description: UNIX timestamp of the block + example: 1506635091 + tx_count: + type: integer + description: Number of transactions in the block + example: 44 + vrf_key: + type: string + description: VRF key of the block producer + example: "vrf_vk1pmxyz8efuyj6eq6zkk373f28u47v06nwp5t59jr5fcmcusaazlmqhxu8k2" + pool: + type: string + description: Pool ID in bech32 format (null for pre-Shelley blocks) + example: pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc + nullable: true + op_cert_counter: + type: integer + description: Counter value of the operational certificate used to create this block + example: 8 + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" + block_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + abs_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_size: + $ref: "#/components/schemas/blocks/items/properties/block_size" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + tx_count: + $ref: "#/components/schemas/blocks/items/properties/tx_count" + vrf_key: + $ref: "#/components/schemas/blocks/items/properties/vrf_key" + op_cert: + type: string + description: Hash of the block producers' operational certificate + example: "16bfc28a7127d11805fe02df67f8c3909ab7e2e2cd81b6954d90eeff1938614c" + op_cert_counter: + $ref: "#/components/schemas/blocks/items/properties/op_cert_counter" + pool: + $ref: "#/components/schemas/blocks/items/properties/pool" + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" + total_output: + type: string + description: Total output of the block (in lovelace) + example: 92384672389 + nullable: true + total_fees: + type: string + description: Total fees of the block (in lovelace) + example: 2346834 + nullable: true + num_confirmations: + type: integer + description: Number of confirmations for the block + example: 664275 + parent_hash: + type: string + description: Hash of the parent of this block + example: "16bfc28a7127d11805fe02df67f8c3909ab7e2e2cd81b6954d90eeff1938614c" + child_hash: + type: string + description: Hash of the child of this block (if present) + example: "a3b525ba0747ce9daa928fa28fbc680f95e6927943a1fbd6fa5394d96c9dc2fa" + block_txs: + type: array + items: + type: object + properties: + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + tx_hashes: + type: array + items: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + address_info: + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + balance: + type: string + description: Sum of all UTxO values beloning to address + example: 10723473983 + stake_address: + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" + script_address: + type: boolean + description: Signifies whether the address is a script address + example: true + utxo_set: + type: array + items: + type: object + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + tx_index: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + value: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + datum_hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" + address_txs: + type: array + items: + type: object + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + address_assets: + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" + credential_txs: + $ref: "#/components/schemas/address_txs" + account_list: + type: array + items: + type: object + properties: + id: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + account_info: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + status: + type: string + description: Stake address status + enum: ["registered", "not registered"] + example: registered + delegated_pool: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + total_balance: + type: string + description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) + example: 207116800428 + utxo: + type: string + description: Total UTxO balance of the account + example: 162764177131 + rewards: + type: string + description: Total rewards earned by the account + example: 56457728047 + withdrawals: + type: string + description: Total rewards withdrawn by the account + example: 12105104750 + rewards_available: + type: string + description: Total rewards available for withdawal + example: 44352623297 + reserves: + type: string + description: Total reserves MIR value of the account + example: "0" + treasury: + type: string + description: Total treasury MIR value of the account + example: "0" + account_rewards: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + rewards: + type: array + items: + type: object + properties: + earned_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + spendable_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + amount: + type: string + description: Amount of rewards earned (in lovelace) + type: + type: string + description: The source of the rewards + enum: [member, leader, treasury, reserves] + example: member + pool_id: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + account_updates: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + updates: + type: array + items: + type: object + properties: + action_type: + type: string + description: Type of certificate submitted + enum: ["registration", "delegation", "withdrawal", "deregistration"] + example: "registration" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + account_addresses: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + addresses: + type: array + items: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + account_assets: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + asset_list: + type: array + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Asset quantity owned by account + example: 990000 + account_history: + type: array + items: + properties: + stake_address: + type: string + description: Cardano staking address (reward account) in bech32 format + example: stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz + history: + type: array + items: + type: object + properties: + pool_id: + type: string + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + epoch_no: + type: integer + description: Epoch number + example: 301 + active_stake: + type: string + description: Active stake amount (in lovelaces) + example: 682334162 + tx_info: + type: array + items: + type: object + properties: + tx_hash: + type: string + description: Hash identifier of the transaction + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + tx_timestamp: + type: integer + description: UNIX timestamp of the transaction + example: 1506635091 + tx_block_index: + type: integer + description: Index of transaction within block + example: 6 + tx_size: + type: integer + description: Size in bytes of transaction + example: 391 + total_output: + type: string + description: Total sum of all transaction outputs (in lovelaces) + example: 157832856 + fee: + type: string + description: Total Transaction fee (in lovelaces) + example: 172761 + deposit: + type: string + description: Total Deposits included in transaction (for example, if it is registering a pool/key) + example: 0 + invalid_before: + type: integer + description: Slot before which transaction cannot be validated (if supplied, else null) + nullable: true + invalid_after: + type: integer + description: Slot after which transaction cannot be validated + example: 42332172 + nullable: true + collateral_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral inputs needed for smart contracts in case of contract failure + collateral_output: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items" + description: A collateral output for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + nullable: true + reference_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) + inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of UTxO inputs spent in the transaction + outputs: + type: array + description: An array of UTxO outputs created by the transaction + items: + type: object + properties: + payment_addr: + type: object + properties: + bech32: + type: string + description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned + example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw + cred: + type: string + description: Payment credential + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 + stake_addr: + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" + tx_hash: + type: string + description: Hash of transaction for UTxO + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + tx_index: + type: integer + description: Index of UTxO in the transaction + example: 0 + value: + type: string + description: Total sum of ADA on the UTxO + example: 157832856 + datum_hash: + type: string + nullable: true + description: Hash of datum (if any) connected to UTxO + example: 30c16dd243324cf9d90ffcf211b9e0f2117a7dc28d17e85927dfe2af3328e5c9 + inline_datum: + type: object + nullable: true + description: Allows datums to be attached to UTxO (CIP-32) + properties: + bytes: + type: string + description: Datum bytes (hex) + example: 19029a + value: + type: object + description: Value (json) + example: { "int": 666 } + reference_script: + type: object + nullable: true + description: Allow reference scripts to be used to satisfy script requirements during validation, rather than requiring the spending transaction to do so. (CIP-33) + properties: + hash: + type: string + description: Hash of referenced script + example: 67f33146617a5e61936081db3b2117cbf59bd2123748f58ac9678656 + size: + type: integer + description: Size in bytes + example: 14 + type: + type: string + description: Type of script + example: plutusV1 + bytes: + type: string + description: Script bytes (hex) + example: 4e4d01000033222220051200120011 + value: + type: object + nullable: true + description: Value (json) + example: null + asset_list: + type: array + nullable: true + description: An array of assets on the UTxO + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Quantity of assets on the UTxO + example: 1 + withdrawals: + type: array + description: Array of withdrawals with-in a transaction + nullable: true + items: + type: object + properties: + amount: + type: string + description: Withdrawal amount (in lovelaces) + example: 9845162 + stake_addr: + type: string + description: A Cardano staking address (reward account, bech32 encoded) + example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + assets_minted: + type: array + description: Array of minted assets with-in a transaction + nullable: true + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Quantity of minted assets (negative on burn) + example: 1 + metadata: + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + certificates: + type: array + nullable: true + description: Certificates present with-in a transaction (if any) + items: + properties: + index: + type: integer + nullable: true + description: Certificate index + example: 0 + type: + type: string + description: Type of certificate (could be delegation, stake_registration, stake_deregistraion, pool_update, pool_retire, param_proposal, reserve_MIR, treasury_MIR) + example: delegation + info: + type: object + description: A JSON array containing information from the certificate + nullable: true + example: + { + "stake_address": "stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj", + "pool": "pool1k53pf4wzn263c08e3wr3gttndfecm9f4uzekgctcx947vt7fh2p", + } + native_scripts: + type: array + nullable: true + description: Native scripts present in a transaction (if any) + items: + properties: + script_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + script_json: + type: object + description: JSON representation of the timelock script (null for other script types) + example: + { + "type": "all", + "scripts": + [ + { + "type": "sig", + "keyHash": "a96da581c39549aeda81f539ac3940ac0cb53657e774ca7e68f15ed9", + }, + { + "type": "sig", + "keyHash": "ccfcb3fed004562be1354c837a4a4b9f4b1c2b6705229efeedd12d4d", + }, + { + "type": "sig", + "keyHash": "74fcd61aecebe36aa6b6cd4314027282fa4b41c3ce8af17d9b77d0d1", + }, + ], + } + plutus_contracts: + type: array + description: Plutus contracts present in transaction (if any) + items: + properties: + address: + type: string + description: Plutus script address + example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 + script_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + bytecode: + type: string + description: CBOR-encoded Plutus script data + example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 + size: + type: integer + description: The size of the CBOR serialised script (in bytes) + example: 234895 + valid_contract: + type: boolean + description: True if the contract is valid or there is no contract + example: true + input: + type: object + properties: + redeemer: + type: object + properties: + purpose: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/purpose" + fee: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/fee" + unit: + type: object + properties: + steps: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/unit_steps" + mem: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/unit_mem" + datum: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + datum: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + output: + type: object + nullable: true + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + tx_utxos: + type: array + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + inputs: + $ref: "#/components/schemas/tx_info/items/properties/inputs" + outputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + tx_metadata: + type: array + nullable: true + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + metadata: + type: object + nullable: true + description: A JSON array containing details about metadata within transaction + example: + { + "721": + { + "version": 1, + "copyright": "...", + "publisher": ["p...o"], + "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d": + {}, + }, + } + tx_status: + type: array + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + num_confirmations: + type: integer + description: Number of block confirmations + example: 17 + nullable: true + tx_metalabels: + type: array + items: + properties: + key: + type: string + description: A distinct known metalabel + example: "721" + asset_list: + type: array + description: Array of policy IDs and asset names + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + asset_address_list: + type: array + description: An array of payment addresses holding the given token (including balances) + items: + properties: + payment_address: + type: string + description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO + example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + quantity: + type: string + description: Asset balance on the payment address + example: 23 + asset_summary: + type: array + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_transactions: + type: integer + description: Total number of transactions including the given asset + example: 89416 + staked_wallets: + type: integer + description: Total number of registered wallets holding the given asset + example: 548 + unstaked_addresses: + type: integer + description: Total number of payment addresses (not belonging to registered wallets) holding the given asset + example: 245 + asset_info: + type: array + items: + properties: + policy_id: + type: string + description: Asset Policy ID (hex) + example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff + asset_name: + type: string + nullable: true + description: Asset Name (hex) + example: 444f4e545350414d + asset_name_ascii: + type: string + description: Asset Name (ASCII) + example: DONTSPAM + fingerprint: + type: string + description: The CIP14 fingerprint of the asset + example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 + minting_tx_hash: + type: string + description: Hash of the first mint transaction + example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 + mint_cnt: + type: integer + description: Count of total mint transactions + example: 1 + burn_cnt: + type: integer + description: Count of total burn transactions + example: 5 + minting_tx_metadata: + allOf: + - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + description: Latest minting transaction metadata (aligns with CIP-25) + token_registry_metadata: + type: object + description: Asset metadata registered on the Cardano Token Registry + nullable: true + properties: + name: + type: string + example: Rackmob + description: + type: string + example: Metaverse Blockchain Cryptocurrency. + ticker: + type: string + example: MOB + url: + type: string + example: https://www.rackmob.com/ + logo: + type: string + description: A PNG image file as a byte string + example: iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADnmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc + decimals: + type: integer + example: 0 + total_supply: + type: string + example: "35000" + creation_time: + type: integer + description: UNIX timestamp of the first asset mint + example: 1506635091 + asset_history: + type: array + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_txs: + type: array + description: Array of all mint/burn transactions for an asset + nullable: true + items: + type: object + properties: + tx_hash: + type: string + description: Hash of minting/burning transaction + example: e1ecc517f95715bb87681cfde2c594dbc971739f84f8bfda16170b35d63d0ddf + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + quantity: + type: string + description: Quantity minted/burned (negative numbers indicate burn transactions) + example: "-10" + metadata: + type: array + description: Array of Transaction Metadata for given transaction + items: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + asset_policy_info: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + asset_name_ascii: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_tx_metadata: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + token_registry_metadata: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" + total_supply: + type: string + example: "35000" + creation_time: + type: string + $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_txs: + type: array + description: An array of Tx hashes that included the given asset (latest first) + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + native_script_list: + type: array + items: + properties: + script_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + creation_tx_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" + type: + type: string + description: Type of the script + enum: ["timelock", "multisig"] + example: timelock + plutus_script_list: + type: array + items: + properties: + script_hash: + type: string + description: Hash of a script + example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 + creation_tx_hash: + type: string + description: Hash of the script creation transaction + example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + script_redeemers: + type: array + items: + type: object + properties: + script_hash: + type: string + description: Hash of Transaction for which details are being shown + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + redeemers: + type: array + items: + type: object + properties: + tx_hash: + type: string + description: Hash of Transaction containing the redeemer + example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + tx_index: + type: integer + description: The index of the redeemer pointer in the transaction + example: 0 + unit_mem: + description: The budget in Memory to run a script + example: 520448 + nullable: true + additionalProperties: + oneOf: + - type: string + - type: integer + unit_steps: + description: The budget in Cpu steps to run a script + example: 211535239 + nullable: true + additionalProperties: + oneOf: + - type: string + - type: integer + fee: + type: string + description: The budget in fees to run a script - the fees depend on the ExUnits and the current prices + example: 45282 + purpose: + type: string + description: What kind of validation this redeemer is used for + enum: ["spend", "mint", "cert", "reward"] + example: spend + datum_hash: + type: string + description: The Hash of the Plutus Data + nullable: true + example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 + datum_value: + type: object + description: The actual data in json format + example: { "bytes": "3c33" } + datum_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + bytes: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" + headers: {} + responses: + OK: + description: Success! + NotFound: + description: The server does not recognise the combination of endpoint and parameters provided + Unauthorized: + description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint + PartialContent: + description: The result was truncated + BadRequest: + description: The server cannot process the request due to invalid input +tags: + - name: Network + description: Query information about the network + x-tag-expanded: false + - name: Epoch + description: Query epoch-specific details + x-tag-expanded: false + - name: Block + description: Query information about particular block on chain + x-tag-expanded: false + - name: Transactions + description: Query blockchain transaction details + x-tag-expanded: false + - name: Address + description: Query information about specific address(es) + x-tag-expanded: false + - name: Account + description: Query details about specific stake account addresses + x-tag-expanded: false + - name: Asset + description: Query Asset related informations + x-tag-expanded: false + - name: Pool + description: Query information about specific pools + x-tag-expanded: false + - name: Script + description: Query information about specific scripts (Smart Contracts) + x-tag-expanded: false +security: [] diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml new file mode 100644 index 00000000..04b840ec --- /dev/null +++ b/specs/results/koiosapi-preview.yaml @@ -0,0 +1,3353 @@ +openapi: 3.0.2 +info: + title: Koios API + version: 1.0.9rc + description: | + Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. + + > Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). + + # Problems solved by Koios + - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) + to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is + free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be + health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. + - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be + consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute + or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that + pass the health-check for the endpoint. + - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For + such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that + take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part + of Koios API. + - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants + automatically receive failover support, they reduce impact of any subset of clusters going through update process. + - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to + give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. + - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will + ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. + - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. + + # Architecture + + ## How does Koios work? + + ![High-Level architecture overview](/koios-design.png) + + We will go bottom to top (from builder's eyes to run through the above) briefly: + + - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. + - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. + - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. + + # API Usage + + The endpoints served by Koios can be browsed from the left side bar of this site. You will find that almost each endpoint has an example that you can `Try` and will help you get an example in shell using cURL. For public queries, you do not need to register yourself - you can simply use them as per the examples provided on individual endpoints. But in addition, the [PostgREST API](https://postgrest.org/en/stable/api.html) used underneath provides a handful of features that can be quite handy for you to improve your queries to directly grab very specific information pertinent to your calls, reducing data you download and process. + + ## Vertical Filtering + + Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

+ + ``` bash + curl "https://api.koios.rest/api/v0/tip" + + # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] + + curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" + + # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] + ``` + + ## Horizontal Filtering + + You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

+ ``` bash + curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" + + # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, + # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] + ``` + + Here, we made use of `eq.` operator to denote a filter of "value equal to" against `epoch` column. Similarly, we added a filter using `lt.` operator to denote a filter of "values lower than" against `epoch_slot` column. You can find a complete list of operators supported in PostgREST documentation (commonly used ones extracted below): + + |Abbreviation|In PostgreSQL|Meaning | + |------------|-------------|-------------------------------------------| + |eq |`=` |equals | + |gt |`>` |greater than | + |gte |`>=` |greater than or equal | + |lt |`<` |less than | + |lte |`<=` |less than or equal | + |neq |`<>` or `!=` |not equal | + |like |`LIKE` |LIKE operator (use * in place of %) | + |in |`IN` |one of a list of values, e.g. `?a=in.("hi,there","yes,you")`| + |is |`IS` |checking for exact equality (null,true,false,unknown)| + |cs |`@>` |contains e.g. `?tags=cs.{example, new}` | + |cd |`<@` |contained in e.g. `?values=cd.{1,2,3}` | + |not |`NOT` |negates another operator | + |or |`OR` |logical `OR` operator | + |and |`AND` |logical `AND` operator | + + ## Pagination (offset/limit) + + When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. + + The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. + + Sounds confusing? Let's see this in practice, to hopefully make it easier. + Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range + + # content-range: 0-999/* + + ``` + + As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range + + # content-range: 1000-1499/* + + ``` + + There is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range + + # content-range: 1000-1499/* + + ``` + + The above methods for pagination are very useful to keep your queries light as well as process the output in smaller pages, making better use of your resources and respecting server timeouts for response times. + + ## Ordering + + You can set a sorting order for returned queries against specific column(s). + Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" + + # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"dc9496eae64294b46f07eb20499ae6dae4d81fdc67c63c354397db91bda1ee55","epoch":236,"abs_slot":16598058,"epoch_slot":9258,"block_height":5086962,"block_time":1608164349,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"6ebc7b734c513bc19290d96ca573a09cac9503c5a349dd9892b9ab43f917f9bd","epoch":236,"abs_slot":16601491,"epoch_slot":12691,"block_height":5087097,"block_time":1608167782,"tx_count":0,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, + # {"hash":"2eac97548829fc312858bc56a40f7ce3bf9b0ca27ee8530283ccebb3963de1c0","epoch":236,"abs_slot":16602308,"epoch_slot":13508,"block_height":5087136,"block_time":1608168599,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}] + ``` + + ## Response Formats + + You can get the results from the PostgREST endpoints in CSV or JSON formats. The default response format will always be JSON, but if you'd like to switch, you can do so by specifying header `'Accept: text/csv'` or `'Accept: application/json'`. + Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

+ + ``` bash + curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" + + # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, + # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, + # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] + + curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" + + # epoch,epoch_slot,block_time + # 318,28491,1643607582 + # 318,28479,1643607570 + # 318,28406,1643607497 + + ``` + + ## Limits + + While use of Koios is completely free and there are no registration requirements to the usage, the monitoring layer will only restrict spam requests that can potentially cause high amount of load to backends. The emphasis is on using list of objects first, and then [bulk where available] query specific objects to drill down where possible - which forms higher performance results to consumer as well as instance provider. Some basic protection against patterns that could cause unexpected resource spikes are protected as per below: + + - Burst Limit: A single IP can query an endpoint up to 100 times within 10 seconds (that's about 8.64 million requests within a day). The sleep time if a limit is crossed is minimal (60 seconds) for that IP - during which, the monitoring layer will return HTTP Status `429 - Too many requests`. + - Pagination/Limits: Any query results fetched will be paginated by 1000 records (you can reduce limit and or control pagination offsets on URL itself, see API > Pagination section for more details). + - Query timeout: If a query from server takes more than 30 seconds, it will return a HTTP Status of `504 - Gateway timeout`. This is because we would want to ensure you're using the queries optimally, and more often than not - it would indicate that particular endpoint is not optimised (or the network connectivity is not optimal between servers). + + Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. + + + # Community projects + + A big thank you to the following projects who are already starting to use Koios from early days: + + ## CLI + + - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) + + ## Libraries + + - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) + - [Go Client](https://github.com/cardano-community/koios-go-client) + - [Java Client](https://github.com/cardano-community/koios-java-client) + + ## Community Projects/Tools + + - [Building On Cardano](https://buildingoncardano.com) + - [CardaStat](cardastat.info) + - [CNFT.IO](https://cnft.io) + - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) + - [Dandelion](https://dandelion.link) + - [Eternl](https://eternl.io/) + - [PoolPeek](https://poolpeek.com) + - [TosiDrop](https://tosidrop.io) + + # FAQ + + ### Is there a price attached to using services? + For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). + + ### Who are the folks behind Koios? + It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) + who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many + for experiments. + + ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? + All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. + + ### I am not sure how to set up an instance. Is there an easy start guide? + Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). + + ### Too much reading, I want to discuss in person + There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. + + x-logo: + url: "https://api.koios.rest/koios.png" +servers: + - url: https://api.koios.rest/api/v0 + - url: https://guild.koios.rest/api/v0 + - url: https://testnet.koios.rest/api/v0 +paths: + /tip: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of block summary (limit+paginated) + content: + application/json: + schema: + $ref: "#/components/schemas/tip" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Query Chain Tip + description: Get the tip info about the latest block seen by chain + /genesis: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of genesis parameters used to start each era on chain + content: + application/json: + schema: + $ref: "#/components/schemas/genesis" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Get Genesis info + description: Get the Genesis parameters used to start specific era on chain + /totals: #RPC + get: + tags: + - Network + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of supply/reserves/utxo/fees/treasury stats + content: + application/json: + schema: + $ref: "#/components/schemas/totals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Get historical tokenomic stats + description: >- + Get the circulating utxo, treasury, rewards, supply and reserves in + lovelace for specified epoch, all epochs if empty + /epoch_info: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of detailed summary for each epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch Information + description: Get the epoch information, all epochs if no epoch specified + /epoch_params: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of protocol parameters for each epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_params" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Protocol Parameters + description: >- + Get the protocol parameters for specific epoch, returns information + about all epochs if no epoch specified + /epoch_block_protocols: #RPC + get: + tags: + - Epoch + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of distinct block protocol versions counts in epoch + content: + application/json: + schema: + $ref: "#/components/schemas/epoch_block_protocols" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Epoch's Block Protocols + description: >- + Get the information about block protocol distribution in epoch + /blocks: + get: + tags: + - Block + responses: + "200": + description: Array of block information + content: + application/json: + schema: + $ref: "#/components/schemas/blocks" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Block List + description: Get summarised details about all blocks (paginated - latest first) + /block_info: #RPC + post: + tags: + - Block + requestBody: + $ref: "#/components/requestBodies/block_hashes" + responses: + "200": + description: Array of detailed block information + content: + application/json: + schema: + $ref: "#/components/schemas/block_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Block Information + description: Get detailed information about a specific block + /block_txs: #RPC + post: + tags: + - Block + requestBody: + $ref: "#/components/requestBodies/block_hashes" + responses: + "200": + description: Array of transactions hashes + content: + application/json: + schema: + $ref: "#/components/schemas/block_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Block Transactions + description: Get a list of all transactions included in provided blocks + /tx_info: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of detailed information about transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Information + description: Get detailed information about transaction(s) + /tx_utxos: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of inputs and outputs for given transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_utxos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction UTxOs + description: Get UTxO set (inputs/outputs) of transactions. + /tx_metadata: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of metadata information present in each of the transactions queried + content: + application/json: + schema: + $ref: "#/components/schemas/tx_metadata" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Metadata + description: Get metadata information (if any) for given transaction(s) + /tx_metalabels: #RPC + get: + tags: + - Transactions + responses: + "200": + description: Array of known metadata labels + content: + application/json: + schema: + $ref: "#/components/schemas/tx_metalabels" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Metadata Labels + description: Get a list of all transaction metalabels + /submittx: #submit-api + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/txbin" + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} is a raw binary serialized transaction on the file-system. + # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/v0/submittx + responses: + "202": + description: OK + content: + application/json: + schema: + description: The transaction id. + type: string + format: hex + minLength: 64 + maxLength: 64 + example: 92bcd06b25dfbd89b578d536b4d3b7dd269b7c2aa206ed518012cffe0444d67f + "400": + description: An error occured while submitting transaction. + summary: Submit Transaction + description: Submit an already serialized transaction to the network. + /tx_status: #RPC + post: + tags: + - Transactions + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of transaction confirmation counts + content: + application/json: + schema: + $ref: "#/components/schemas/tx_status" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction Status (Block Confirmations) + description: Get the number of block confirmations for a given transaction hash list + /address_info: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address information + content: + application/json: + schema: + $ref: "#/components/schemas/address_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Information + description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses + /address_txs: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/address_txs" + responses: + "200": + description: Array of transaction hashes + content: + application/json: + schema: + $ref: "#/components/schemas/address_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Transactions + description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) + /address_assets: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address-owned assets + content: + application/json: + schema: + $ref: "#/components/schemas/address_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Assets + description: Get the list of all the assets (policy, name and quantity) for given addresses + /credential_txs: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/credential_txs" + responses: + "200": + description: Array of transaction hashes + content: + application/json: + schema: + $ref: "#/components/schemas/credential_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transactions from payment credentials + description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) + /account_list: + get: + tags: + - Account + responses: + "200": + description: Array of account (stake address) IDs + content: + application/json: + schema: + $ref: "#/components/schemas/account_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account List + description: Get a list of all accounts + /account_info: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information + description: Get the account information for given stake addresses (accounts) + /account_info_cached: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (accounts) + /account_rewards: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + responses: + "200": + description: Array of reward history information + content: + application/json: + schema: + $ref: "#/components/schemas/account_rewards" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Rewards + description: >- + Get the full rewards history (including MIR) for given stake addresses (accounts) + /account_updates: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account updates information + content: + application/json: + schema: + $ref: "#/components/schemas/account_updates" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Updates + description: >- + Get the account updates (registration, deregistration, delegation and + withdrawals) for given stake addresses (accounts) + /account_addresses: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of payment addresses + content: + application/json: + schema: + $ref: "#/components/schemas/account_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Addresses + description: Get all addresses associated with given staking accounts + /account_assets: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of assets owned by account + content: + application/json: + schema: + $ref: "#/components/schemas/account_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Assets + description: Get the native asset balance of given accounts + /account_history: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" + responses: + "200": + description: Array of active stake values per epoch + content: + application/json: + schema: + $ref: "#/components/schemas/account_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account History + description: Get the staking history of given stake addresses (accounts) + /asset_list: + get: + tags: + - Asset + responses: + "200": + description: Array of policy IDs and asset names + content: + application/json: + schema: + $ref: "#/components/schemas/asset_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset List + description: Get the list of all native assets (paginated) + /asset_address_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of payment addresses holding the given token (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/asset_address_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Address List + description: Get the list of all addresses holding a given asset + /asset_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information + description: Get the information of an asset including first minting & token registry metadata + /asset_history: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of asset mint/burn history + content: + application/json: + schema: + $ref: "#/components/schemas/asset_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset History + description: Get the mint/burn history of an asset + /asset_policy_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/asset_policy_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Policy Information + description: Get the information for all assets under the same policy + /asset_summary: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of asset summary information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_summary" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Summary + description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) + /asset_txs: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" + responses: + "200": + description: Array of Tx hashes that included the given asset + content: + application/json: + schema: + $ref: "#/components/schemas/asset_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) + /pool_list: #RPC + get: + tags: + - Pool + responses: + "200": + description: Array of pool IDs and tickers + content: + application/json: + schema: + $ref: "#/components/schemas/pool_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool List + description: A list of all currently registered/retiring (not retired) pools + /pool_info: #RPC + post: + tags: + - Pool + requestBody: + $ref: "#/components/requestBodies/pool_ids" + responses: + "200": + description: Array of pool information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Information + description: Current pool statuses and details for a specified list of pool ids + /pool_stake_snapshot: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool stake information for 3 snapshots + content: + application/json: + schema: + $ref: "#/components/schemas/pool_snapshot" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake Snapshot + description: Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation + /pool_delegators: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators List + description: Return information about live delegators for a given pool. + /pool_delegators_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool delegator information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_delegators_history" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Delegators History + description: Return information about active delegators (incl. history) for a given pool and epoch number (all epochs if not specified). + /pool_blocks: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of blocks created by pool + content: + application/json: + schema: + $ref: "#/components/schemas/pool_blocks" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Blocks + description: >- + Return information about blocks minted by a given pool for all epochs + (or _epoch_no if provided) + /pool_history: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32" + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of pool history information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_history_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Stake, Block and Reward History + description: >- + Return information about pool stake, block and reward history in a given epoch _epoch_no + (or all epochs that pool existed for, in descending order if no _epoch_no was provided) + /pool_updates: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_pool_bech32_optional" + required: false + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_updates" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Updates (History) + description: Return all pool updates for all pools or only updates for specific pool if specified + /pool_relays: #RPC + get: + tags: + - Pool + responses: + "200": + description: Array of pool relay information + content: + application/json: + schema: + $ref: "#/components/schemas/pool_relays" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Relays + description: A list of registered relays for all currently registered/retiring (not retired) pools + /pool_metadata: #RPC + post: + tags: + - Pool + requestBody: + $ref: "#/components/requestBodies/pool_ids_optional" + responses: + "200": + description: Array of pool metadata + content: + application/json: + schema: + $ref: "#/components/schemas/pool_metadata" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Metadata + description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools + /native_script_list: #RPC + get: + tags: + - Script + responses: + "200": + description: List of native script and creation tx hash pairs + content: + application/json: + schema: + $ref: "#/components/schemas/native_script_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Native Script List + description: List of all existing native script hashes along with their creation transaction hashes + /plutus_script_list: #RPC + get: + tags: + - Script + responses: + "200": + description: List of Plutus script and creation tx hash pairs + content: + application/json: + schema: + $ref: "#/components/schemas/plutus_script_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Plutus Script List + description: List of all existing Plutus script hashes along with their creation transaction hashes + /script_redeemers: #RPC + get: + tags: + - Script + parameters: + - $ref: "#/components/parameters/_script_hash" + responses: + "200": + description: List of all redeemers for a given script hash + content: + application/json: + schema: + $ref: "#/components/schemas/script_redeemers" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Script Redeemers + description: List of all redeemers for a given script hash + /datum_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/datum_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/datum_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes +components: + parameters: + select: + name: select + description: Filtering Columns + schema: + type: string + in: query + required: false + on_conflict: + name: on_conflict + description: On Conflict + schema: + type: string + in: query + required: false + order: + name: order + description: Ordering + schema: + type: string + in: query + required: false + range: + name: Range + description: Limiting and Pagination + schema: + type: string + in: header + required: false + rangeUnit: + name: Range-Unit + description: Limiting and Pagination + schema: + default: items + type: string + in: header + required: false + offset: + name: offset + description: Limiting and Pagination + schema: + type: string + in: query + required: false + limit: + name: limit + description: Limiting and Pagination + schema: + type: string + in: query + required: false + example: 10 + _block_hash: + deprecated: false + name: _block_hash + description: Block Hash in hex format + schema: + type: string + example: 6d6e3722c81a1b9bca9dd9b7f63cae096bd1f97f6ef1d67c178bacab1a248483 + in: query + required: true + allowEmptyValue: false + _after_block_height: + deprecated: false + name: _after_block_height + description: Block height for specifying time delta + schema: + type: integer + example: 50000 + in: query + required: false + allowEmptyValue: true + _epoch_no: + deprecated: false + name: _epoch_no + description: Epoch Number to fetch details for + schema: + type: string + example: 12 + in: query + required: false + allowEmptyValue: true + _earned_epoch_no: + deprecated: false + name: _epoch_no + description: Filter for earned rewards Epoch Number + schema: + type: string + example: 8 + in: query + required: false + allowEmptyValue: true + _any_address: + deprecated: false + name: _address + description: Cardano payment or staking address in bech32 format + schema: + type: string + example: stake_test1uqd2nz8ugrn6kwkflvmt9he8dr966dszfmm5lt66qdmn28qt4wff9 + in: query + required: true + allowEmptyValue: false + _address: + deprecated: false + name: _address + description: Cardano payment address in bech32 format + schema: + type: string + example: addr_test1qzrxlwtmf4zzqndw6trwlgmyhnk8hncdx9w6ls9vvgy4xa0am5at75hv9zravxzwf5wlalxqm4jlr6u8qkp4t2unmdpsjvpe2n + in: query + required: true + allowEmptyValue: false + _address_assets: + deprecated: false + name: _address + description: Cardano payment address in bech32 format + schema: + type: string + example: addr_test1qphcrpt99pu2g2pwv32k8xqxa86pqswv2sa4tqefvruyfzeg3292mxuf3kq7nysjumlxjrlsfn9tp85r0l54l29x3qcsxxn580 + in: query + required: true + allowEmptyValue: false + _stake_address: + deprecated: false + name: _stake_address + description: Cardano staking address (reward account) in bech32 format + schema: + type: string + example: stake_test1uzs5rxys8qy5jnr9g0mkj860ms5n92nrykmrgyumpf2ytmsejj4m6 + in: query + required: true + allowEmptyValue: false + _asset_policy: + deprecated: false + name: _asset_policy + description: Asset Policy ID in hexadecimal format (hex) + schema: + type: string + example: 065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404 + in: query + required: true + allowEmptyValue: false + _asset_name: + deprecated: false + name: _asset_name + description: Asset Name in hexadecimal format (hex), empty asset name returns royalties + schema: + type: string + example: "433374" + in: query + required: false + allowEmptyValue: true + _history: + deprecated: false + name: _history + description: Include all historical transactions, setting to false includes only the non-empty ones + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: false + _pool_bech32: + deprecated: false + name: _pool_bech32 + description: Pool ID in bech32 format + schema: + type: string + example: pool1leml52hm4fcp3hhe4zye08qz27llhj7d339p3gs0tl85cstx59q + in: query + required: true + allowEmptyValue: false + _pool_bech32_optional: + deprecated: false + name: _pool_bech32 + description: Pool ID in bech32 format (optional) + schema: + type: string + example: pool1leml52hm4fcp3hhe4zye08qz27llhj7d339p3gs0tl85cstx59q + in: query + required: false + allowEmptyValue: true + _script_hash: + deprecated: false + name: _script_hash + description: Script hash in hexadecimal format (hex) + schema: + type: string + example: f758cf422ca0cbed7d9d6fad1eb5a3c70537d62e661ad450dd2a3810 + in: query + required: true + allowEmptyValue: false + requestBodies: + block_hashes: + content: + application/json: + schema: + required: + - _block_hashes + type: object + properties: + _block_hashes: + format: text + type: array + items: + $ref: "#/components/schemas/blocks/items/properties/hash" + example: + _block_hashes: + - a4504e2495ed03b48be36676f430c54dca0769d29f72ebf18d493abf42d2167b + - 8e7a6206d2b21ae4f26e7e09353fadae17f838a63d095c2be51acbd16e9b7716 + - 1baaf7812ed48e663adb9eeaa68fe25034e5e30b4f8e56cc8600cac5e9d42ce7 + payment_addresses: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + example: + _addresses: + - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc + - addr_test1vqneq3v0dqh3x3muv6ee3lt8e5729xymnxuavx6tndcjc2cv24ef9 + address_txs: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + _after_block_height: + format: integer + type: integer + description: Only fetch information after specific block height + example: + _addresses: + - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc + - addr_test1vqneq3v0dqh3x3muv6ee3lt8e5729xymnxuavx6tndcjc2cv24ef9 + _after_block_height: 40356 + stake_addresses_with_epoch_no: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _epoch_no: + format: integer + type: integer + description: Only fetch information for a specific epoch + example: + _stake_addresses: + - stake_test1upv7n2x0lxepkyx8ux2gjt74ecaa39tjgaccxl6hw5fwzngpzf5zt + - stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl + _epoch_no: 11 + stake_addresses: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + example: + _stake_addresses: + - stake_test1upv7n2x0lxepkyx8ux2gjt74ecaa39tjgaccxl6hw5fwzngpzf5zt + - stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl + credential_txs: + content: + application/json: + schema: + required: + - _payment_credentials + type: object + properties: + _payment_credentials: + format: text + type: array + items: + type: string + description: Array of Cardano payment credential(s) in hex format + _after_block_height: + format: integer + type: integer + description: Only fetch information after specific block height + example: + _payment_credentials: + - 33c378cee41b2e15ac848f7f6f1d2f78155ab12d93b713de898d855f + - 52e63f22c5107ed776b70f7b92248b02552fd08f3e747bc745099441 + _after_block_height: 40356 + tx_ids: + content: + application/json: + schema: + required: + - _tx_hashes + type: object + properties: + _tx_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano Transaction hashes + example: + _tx_hashes: + - f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c + - 206f6da5b0b0de45605a95f5ce7e172be9674550f7dde3838c45cbf24bab8b00 + txbin: + content: + application/cbor: + schema: + type: string + format: binary + example: f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c + pool_ids: + content: + application/json: + schema: + required: + - _pool_bech32_ids + type: object + properties: + _pool_bech32_ids: + format: text + type: array + items: + type: string + description: Array of Cardano pool IDs (bech32 format) + example: + _pool_bech32_ids: + - pool1p90428kec03mjdya3k4gv5d20w7lmed7ca0snknef5j977l3y8l + - pool1wwh3k3ldzujdvgxllfwlnnkxyheafkacqlufnvpr77n5q72f9hw + - pool1p835jxsj8py5n34lrgk6fvpgpxxvh585qm8dzvp7ups37vdet5a + pool_ids_optional: + content: + application/json: + schema: + type: object + properties: + _pool_bech32_ids: + format: text + type: array + items: + type: string + description: Array of Cardano pool IDs (bech32 format) + example: + _pool_bech32_ids: + - pool1p90428kec03mjdya3k4gv5d20w7lmed7ca0snknef5j977l3y8l + - pool1wwh3k3ldzujdvgxllfwlnnkxyheafkacqlufnvpr77n5q72f9hw + - pool1p835jxsj8py5n34lrgk6fvpgpxxvh585qm8dzvp7ups37vdet5a + datum_hashes: + content: + application/json: + schema: + type: object + properties: + _datum_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano datum hashes + example: + _datum_hashes: + - 6181b3dc623cd8812caf027a3507e9b3095388a7cf3db65983e1fddd3a84c88c + - f8ae55ff89e1f5366f23e16bcaf2073252337b96031a02d79e41d653b5f0a978 + securitySchemes: {} + schemas: + tip: + type: array + items: + properties: + hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + abs_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + block_no: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + genesis: + type: array + items: + properties: + networkmagic: + type: string + example: 764824073 + description: Unique network identifier for chain + networkid: + type: string + example: Mainnet + description: Network ID used at various CLI identification to distinguish between Mainnet and other networks + epochlength: + type: string + example: 432000 + description: Number of slots in an epoch + slotlength: + type: string + example: 1 + description: Duration of a single slot (in seconds) + maxlovelacesupply: + type: string + example: 45000000000000000 + description: Maximum smallest units (lovelaces) supply for the blockchain + systemstart: + type: integer + description: UNIX timestamp of the first block (genesis) on chain + example: 1506203091 + activeslotcoeff: + type: string + example: 0.05 + description: "Active Slot Co-Efficient (f) - determines the _probability_ of number of slots in epoch that are expected to have blocks (so mainnet, this would be: 432000 * 0.05 = 21600 estimated blocks)" + slotsperkesperiod: + type: string + example: 129600 + description: Number of slots that represent a single KES period (a unit used for validation of KES key evolutions) + maxkesrevolutions: + type: string + example: 62 + description: Number of KES key evolutions that will automatically occur before a KES (hot) key is expired. This parameter is for security of a pool, in case an operator had access to his hot(online) machine compromised + securityparam: + type: string + example: 2160 + description: A unit (k) used to divide epochs to determine stability window (used in security checks like ensuring atleast 1 block was created in 3*k/f period, or to finalize next epoch's nonce at 4*k/f slots before end of epoch) + updatequorum: + type: string + example: 5 + description: Number of BFT members that need to approve (via vote) a Protocol Update Proposal + alonzogenesis: + type: string + example: '{\"lovelacePerUTxOWord\":34482,\"executionPrices\":{\"prSteps\":{\"numerator\":721,\"denominator\":10000000},...' + description: A JSON dump of Alonzo Genesis + totals: + type: array + items: + properties: + epoch_no: + type: integer + description: Epoch number + example: 294 + circulation: + type: string + description: Circulating UTxOs for given epoch (in lovelaces) + example: 32081169442642320 + treasury: + type: string + description: Funds in treasury for given epoch (in lovelaces) + example: 637024173474141 + reward: + type: string + description: Rewards accumulated as of given epoch (in lovelaces) + example: 506871250479840 + supply: + type: string + description: Total Active Supply (sum of treasury funds, rewards, UTxOs, deposits and fees) for given epoch (in lovelaces) + example: 33228495612391330 + reserves: + type: string + description: Total Reserves yet to be unlocked on chain + example: 11771504387608670 + pool_list: + type: array + items: + properties: + pool_id_bech32: + type: string + nullable: true + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + ticker: + type: string + nullable: true + description: Pool ticker + example: JAZZ + pool_history_info: + type: array + items: + type: object + properties: + epoch_no: + type: integer + description: Epoch for which the pool history data is shown + example: 312 + active_stake: + type: string + description: Amount of delegated stake to this pool at the time of epoch snapshot (in lovelaces) + example: "31235800000" + active_stake_pct: + type: number + description: Active stake for the pool, expressed as a percentage of total active stake on network + example: 13.512182543475783 + saturation_pct: + type: number + description: Saturation percentage of a pool at the time of snapshot (2 decimals) + example: 45.32 + block_cnt: + type: integer + nullable: true + description: Number of blocks pool created in that epoch + example: 14 + delegator_cnt: + type: integer + description: Number of delegators to the pool for that epoch snapshot + example: 1432 + margin: + type: number + description: Margin (decimal format) + example: 0.125 + fixed_cost: + type: string + description: Pool fixed cost per epoch (in lovelaces) + example: "340000000" + pool_fees: + type: string + description: Total amount of fees earned by pool owners in that epoch (in lovelaces) + example: "123327382" + deleg_rewards: + type: string + description: Total amount of rewards earned by delegators in that epoch (in lovelaces) + example: "123456789123" + epoch_ros: + type: number + description: Annualized ROS (return on staking) for delegators for this epoch + example: 3.000340466 + pool_info: + type: array + items: + type: object + properties: + pool_id_bech32: + type: string + description: Pool ID (bech32 format) + example: pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc + pool_id_hex: + type: string + description: Pool ID (Hex format) + example: a532904ca60e13e88437b58e7c6ff66b8d5e7ec8d3f4b9e4be7820ec + active_epoch_no: + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" + vrf_key_hash: + type: string + description: Pool VRF key hash + example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 + margin: + type: number + description: Margin (decimal format) + example: 0.1 + fixed_cost: + type: string + description: Pool fixed cost per epoch + example: "500000000" + pledge: + type: string + description: Pool pledge in lovelace + example: "64000000000000" + reward_addr: + type: string + description: Pool reward address + example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 + owners: + type: array + items: + type: string + description: Pool (co)owner address + example: stake1u8088wvudd7dp3rxl0v9xgng8r3j50s65ge3l3jvgd94keqfm3nv3 + relays: + type: array + items: + type: object + properties: + dns: + type: string + nullable: true + description: DNS name of the relay (nullable) + example: relays-new.cardano-mainnet.iohk.io + srv: + type: string + nullable: true + description: DNS service name of the relay (nullable) + example: biostakingpool3.hopto.org + ipv4: + type: string + nullable: true + description: IPv4 address of the relay (nullable) + example: "54.220.20.40" + ipv6: + type: string + nullable: true + description: IPv6 address of the relay (nullable) + example: 2604:ed40:1000:1711:6082:78ff:fe0c:ebf + port: + type: number + nullable: true + description: Port number of the relay (nullable) + example: 6000 + meta_url: + type: string + description: Pool metadata URL + nullable: true + example: https://pools.iohk.io/IOGP.json + meta_hash: + type: string + description: Pool metadata hash + nullable: true + example: 37eb004c0dd8a221ac3598ca1c6d6257fb5207ae9857b7c163ae0f39259d6cc0 + meta_json: + type: object + nullable: true + properties: + name: + type: string + description: Pool name + example: Input Output Global (IOHK) - Private + ticker: + type: string + description: Pool ticker + example: IOGP + homepage: + type: string + description: Pool homepage URL + example: https://iohk.io + description: + type: string + description: Pool description + example: Our mission is to provide economic identity to the billions of people who lack it. IOHK will not use the IOHK ticker. + pool_status: + type: string + description: Pool status + enum: ["registered", "retiring", "retired"] + example: registered + retiring_epoch: + type: integer + description: Announced retiring epoch (nullable) + example: null + nullable: true + op_cert: + type: string + nullable: true + description: Pool latest operational certificate hash + example: 37eb004c0dd8a221ac3598ca1c6d6257fb5207ae9857b7c163ae0f39259d6cc0 + op_cert_counter: + type: integer + nullable: true + description: Pool latest operational certificate counter value + example: 8 + active_stake: + type: string + nullable: true + description: Pool active stake (will be null post epoch transition until dbsync calculation is complete) + example: "64328627680963" + sigma: + type: number + nullable: true + description: Pool relative active stake share + example: 0.0034839235 + block_count: + type: integer + nullable: true + description: Total pool blocks on chain + example: 4509 + live_pledge: + type: string + nullable: true + description: Summary of account balance for all pool owner's + example: "64328594406327" + live_stake: + type: string + nullable: true + description: Pool live stake + example: "64328627680963" + live_delegators: + type: integer + description: Pool live delegator count + example: 5 + live_saturation: + type: number + nullable: true + description: Pool live saturation (decimal format) + example: 94.52 + pool_snapshot: + type: array + nullable: true + items: + type: object + properties: + snapshot: + type: string + description: Type of snapshot ("Mark", "Set" or "Go") + example: "Mark" + epoch_no: + type: integer + description: Epoch number for the snapshot entry + example: 324 + nonce: + $ref: "#/components/schemas/epoch_params/items/properties/nonce" + pool_stake: + type: string + description: Pool's Active Stake for the given epoch + example: "100000000000" + active_stake: + type: string + description: Total Active Stake for the given epoch + example: "103703246364020" + pool_delegators: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + type: string + description: Current delegator live stake (in lovelace) + example: 64328591517480 + active_epoch_no: + type: integer + description: Epoch number in which the delegation becomes active + example: 324 + latest_delegation_hash: + type: string + description: Latest transaction hash used for delegation by the account + example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_delegators_history: + type: array + nullable: true + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + epoch_no: + type: integer + description: Epoch number for the delegation history + example: 324 + pool_blocks: + type: array + nullable: true + items: + type: object + properties: + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + abs_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + pool_updates: + type: array + items: + type: object + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + pool_id_hex: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" + active_epoch_no: + type: integer + description: Epoch number in which the update becomes active + example: 324 + vrf_key_hash: + $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" + margin: + $ref: "#/components/schemas/pool_info/items/properties/margin" + fixed_cost: + $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" + pledge: + $ref: "#/components/schemas/pool_info/items/properties/pledge" + reward_addr: + $ref: "#/components/schemas/pool_info/items/properties/reward_addr" + owners: + $ref: "#/components/schemas/pool_info/items/properties/owners" + relays: + $ref: "#/components/schemas/pool_info/items/properties/relays" + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/meta_url" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" + retiring_epoch: + $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" + pool_relays: + type: array + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + relays: + $ref: "#/components/schemas/pool_info/items/properties/relays" + pool_metadata: + type: array + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/meta_url" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/meta_hash" + meta_json: + $ref: "#/components/schemas/pool_info/items/properties/meta_json" + epoch_info: + type: array + items: + type: object + properties: + epoch_no: + type: integer + description: Epoch number + example: 294 + out_sum: + type: string + description: Total output value across all transactions in epoch + example: 15432725054364942 + fees: + type: string + description: Total fees incurred by transactions in epoch + example: 74325855210 + tx_count: + type: integer + description: Number of transactions submitted in epoch + example: 357919 + blk_count: + type: integer + description: Number of blocks created in epoch + example: 17321 + start_time: + type: integer + description: UNIX timestamp of the epoch start + example: 1506203091 + end_time: + type: integer + description: UNIX timestamp of the epoch end + example: 1506635091 + first_block_time: + type: integer + description: UNIX timestamp of the epoch's first block + example: 1506635091 + last_block_time: + type: integer + description: UNIX timestamp of the epoch's last block + example: 1506635091 + active_stake: + type: string + description: Total active stake in epoch stake snapshot (null for pre-Shelley epochs) + example: 23395112387185880 + nullable: true + total_rewards: + type: string + description: Total rewards earned in epoch (null for pre-Shelley epochs) + example: 252902897534230 + nullable: true + avg_blk_reward: + type: string + description: Average block reward for epoch (null for pre-Shelley epochs) + example: 660233450 + nullable: true + epoch_params: + type: array + items: + properties: + epoch_no: + type: integer + description: Epoch number + example: 294 + min_fee_a: + type: integer + description: The 'a' parameter to calculate the minimum transaction fee + example: 44 + nullable: true + min_fee_b: + type: integer + description: The 'b' parameter to calculate the minimum transaction fee + example: 155381 + nullable: true + max_block_size: + type: integer + description: The maximum block size (in bytes) + example: 65536 + nullable: true + max_tx_size: + type: integer + description: The maximum transaction size (in bytes) + example: 16384 + nullable: true + max_bh_size: + type: integer + description: The maximum block header size (in bytes) + example: 1100 + nullable: true + key_deposit: + type: string + description: The amount (in lovelace) required for a deposit to register a stake address + example: 2000000 + nullable: true + pool_deposit: + type: string + description: The amount (in lovelace) required for a deposit to register a stake pool + example: 500000000 + nullable: true + max_epoch: + type: integer + description: The maximum number of epochs in the future that a pool retirement is allowed to be scheduled for + example: 18 + nullable: true + optimal_pool_count: + type: integer + description: The optimal number of stake pools + example: 500 + nullable: true + influence: + type: number + format: double + description: The pledge influence on pool rewards + example: 0.3 + nullable: true + monetary_expand_rate: + type: number + format: double + description: The monetary expansion rate + example: 0.003 + nullable: true + treasury_growth_rate: + type: number + format: double + description: The treasury growth rate + example: 0.2 + nullable: true + decentralisation: + type: number + format: double + description: The decentralisation parameter (1 fully centralised, 0 fully decentralised) + example: 0.1 + nullable: true + extra_entropy: + type: string + description: The hash of 32-byte string of extra random-ness added into the protocol's entropy pool + example: d982e06fd33e7440b43cefad529b7ecafbaa255e38178ad4189a37e4ce9bf1fa + nullable: true + protocol_major: + type: integer + description: The protocol major version + example: 5 + nullable: true + protocol_minor: + type: integer + description: The protocol minor version + example: 0 + nullable: true + min_utxo_value: + type: string + description: The minimum value of a UTxO entry + example: 34482 + nullable: true + min_pool_cost: + type: string + description: The minimum pool cost + example: 340000000 + nullable: true + nonce: + type: string + description: The nonce value for this epoch + example: 01304ddf5613166be96fce27be110747f2c8fcb38776618ee79225ccb59b81e2 + nullable: true + block_hash: + type: string + description: The hash of the first block where these parameters are valid + example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 + cost_models: + type: string + description: The per language cost models + example: null + nullable: true + price_mem: + type: number + format: double + description: The per word cost of script memory usage + example: 0.0577 + nullable: true + price_step: + type: number + format: double + description: The cost of script execution step usage + example: 7.21e-05 + nullable: true + max_tx_ex_mem: + type: number + description: The maximum number of execution memory allowed to be used in a single transaction + example: 10000000 + nullable: true + max_tx_ex_steps: + type: number + description: The maximum number of execution steps allowed to be used in a single transaction + example: 10000000000 + nullable: true + max_block_ex_mem: + type: number + description: The maximum number of execution memory allowed to be used in a single block + example: 50000000 + nullable: true + max_block_ex_steps: + type: number + description: The maximum number of execution steps allowed to be used in a single block + example: 40000000000 + nullable: true + max_val_size: + type: number + description: The maximum Val size + example: 5000 + nullable: true + collateral_percent: + type: integer + description: The percentage of the tx fee which must be provided as collateral when including non-native scripts + example: 150 + nullable: true + max_collateral_inputs: + type: integer + description: The maximum number of collateral inputs allowed in a transaction + example: 3 + nullable: true + coins_per_utxo_size: + type: string + description: The cost per UTxO size + example: 34482 + nullable: true + epoch_block_protocols: + type: array + items: + properties: + proto_major: + type: integer + description: Protocol major version + example: 6 + proto_minor: + type: integer + description: Protocol major version + example: 2 + blocks: + type: integer + description: Amount of blocks with specified major and protocol combination + example: 2183 + blocks: + type: array + items: + type: object + properties: + hash: + type: string + description: Hash of the block + example: e8c6992d52cd74b577b79251e0351be25070797a0dbc486b2c284d0bf7aeea9c + epoch_no: + type: integer + description: Epoch number of the block + example: 321 + abs_slot: + type: integer + description: Absolute slot number of the block + example: 53384242 + epoch_slot: + type: integer + description: Slot number of the block in epoch + example: 75442 + block_height: + type: integer + description: Block height + example: 42325043 + block_size: + type: integer + description: Block size in bytes + example: 79109 + block_time: + type: integer + description: UNIX timestamp of the block + example: 1506635091 + tx_count: + type: integer + description: Number of transactions in the block + example: 44 + vrf_key: + type: string + description: VRF key of the block producer + example: "vrf_vk1pmxyz8efuyj6eq6zkk373f28u47v06nwp5t59jr5fcmcusaazlmqhxu8k2" + pool: + type: string + description: Pool ID in bech32 format (null for pre-Shelley blocks) + example: pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc + nullable: true + op_cert_counter: + type: integer + description: Counter value of the operational certificate used to create this block + example: 8 + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" + block_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + abs_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_size: + $ref: "#/components/schemas/blocks/items/properties/block_size" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + tx_count: + $ref: "#/components/schemas/blocks/items/properties/tx_count" + vrf_key: + $ref: "#/components/schemas/blocks/items/properties/vrf_key" + op_cert: + type: string + description: Hash of the block producers' operational certificate + example: "16bfc28a7127d11805fe02df67f8c3909ab7e2e2cd81b6954d90eeff1938614c" + op_cert_counter: + $ref: "#/components/schemas/blocks/items/properties/op_cert_counter" + pool: + $ref: "#/components/schemas/blocks/items/properties/pool" + proto_major: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" + proto_minor: + $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" + total_output: + type: string + description: Total output of the block (in lovelace) + example: 92384672389 + nullable: true + total_fees: + type: string + description: Total fees of the block (in lovelace) + example: 2346834 + nullable: true + num_confirmations: + type: integer + description: Number of confirmations for the block + example: 664275 + parent_hash: + type: string + description: Hash of the parent of this block + example: "16bfc28a7127d11805fe02df67f8c3909ab7e2e2cd81b6954d90eeff1938614c" + child_hash: + type: string + description: Hash of the child of this block (if present) + example: "a3b525ba0747ce9daa928fa28fbc680f95e6927943a1fbd6fa5394d96c9dc2fa" + block_txs: + type: array + items: + type: object + properties: + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + tx_hashes: + type: array + items: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + address_info: + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + balance: + type: string + description: Sum of all UTxO values beloning to address + example: 10723473983 + stake_address: + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" + script_address: + type: boolean + description: Signifies whether the address is a script address + example: true + utxo_set: + type: array + items: + type: object + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + tx_index: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + value: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + datum_hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" + address_txs: + type: array + items: + type: object + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + address_assets: + type: array + items: + type: object + properties: + address: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + asset_list: + $ref: "#/components/schemas/account_assets/items/properties/asset_list" + credential_txs: + $ref: "#/components/schemas/address_txs" + account_list: + type: array + items: + type: object + properties: + id: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + account_info: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + status: + type: string + description: Stake address status + enum: ["registered", "not registered"] + example: registered + delegated_pool: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + total_balance: + type: string + description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) + example: 207116800428 + utxo: + type: string + description: Total UTxO balance of the account + example: 162764177131 + rewards: + type: string + description: Total rewards earned by the account + example: 56457728047 + withdrawals: + type: string + description: Total rewards withdrawn by the account + example: 12105104750 + rewards_available: + type: string + description: Total rewards available for withdawal + example: 44352623297 + reserves: + type: string + description: Total reserves MIR value of the account + example: "0" + treasury: + type: string + description: Total treasury MIR value of the account + example: "0" + account_rewards: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + rewards: + type: array + items: + type: object + properties: + earned_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + spendable_epoch: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + amount: + type: string + description: Amount of rewards earned (in lovelace) + type: + type: string + description: The source of the rewards + enum: [member, leader, treasury, reserves] + example: member + pool_id: + $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + account_updates: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + updates: + type: array + items: + type: object + properties: + action_type: + type: string + description: Type of certificate submitted + enum: ["registration", "delegation", "withdrawal", "deregistration"] + example: "registration" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + account_addresses: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + addresses: + type: array + items: + $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + account_assets: + type: array + items: + type: object + properties: + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" + asset_list: + type: array + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Asset quantity owned by account + example: 990000 + account_history: + type: array + items: + properties: + stake_address: + type: string + description: Cardano staking address (reward account) in bech32 format + example: stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz + history: + type: array + items: + type: object + properties: + pool_id: + type: string + description: Bech32 representation of pool ID + example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + epoch_no: + type: integer + description: Epoch number + example: 301 + active_stake: + type: string + description: Active stake amount (in lovelaces) + example: 682334162 + tx_info: + type: array + items: + type: object + properties: + tx_hash: + type: string + description: Hash identifier of the transaction + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + absolute_slot: + $ref: "#/components/schemas/blocks/items/properties/abs_slot" + tx_timestamp: + type: integer + description: UNIX timestamp of the transaction + example: 1506635091 + tx_block_index: + type: integer + description: Index of transaction within block + example: 6 + tx_size: + type: integer + description: Size in bytes of transaction + example: 391 + total_output: + type: string + description: Total sum of all transaction outputs (in lovelaces) + example: 157832856 + fee: + type: string + description: Total Transaction fee (in lovelaces) + example: 172761 + deposit: + type: string + description: Total Deposits included in transaction (for example, if it is registering a pool/key) + example: 0 + invalid_before: + type: integer + description: Slot before which transaction cannot be validated (if supplied, else null) + nullable: true + invalid_after: + type: integer + description: Slot after which transaction cannot be validated + example: 42332172 + nullable: true + collateral_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of collateral inputs needed for smart contracts in case of contract failure + collateral_output: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items" + description: A collateral output for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) + nullable: true + reference_inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) + inputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + description: An array of UTxO inputs spent in the transaction + outputs: + type: array + description: An array of UTxO outputs created by the transaction + items: + type: object + properties: + payment_addr: + type: object + properties: + bech32: + type: string + description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned + example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw + cred: + type: string + description: Payment credential + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 + stake_addr: + nullable: true + allOf: + - $ref: "#/components/schemas/account_history/items/properties/stake_address" + tx_hash: + type: string + description: Hash of transaction for UTxO + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + tx_index: + type: integer + description: Index of UTxO in the transaction + example: 0 + value: + type: string + description: Total sum of ADA on the UTxO + example: 157832856 + datum_hash: + type: string + nullable: true + description: Hash of datum (if any) connected to UTxO + example: 30c16dd243324cf9d90ffcf211b9e0f2117a7dc28d17e85927dfe2af3328e5c9 + inline_datum: + type: object + nullable: true + description: Allows datums to be attached to UTxO (CIP-32) + properties: + bytes: + type: string + description: Datum bytes (hex) + example: 19029a + value: + type: object + description: Value (json) + example: { "int": 666 } + reference_script: + type: object + nullable: true + description: Allow reference scripts to be used to satisfy script requirements during validation, rather than requiring the spending transaction to do so. (CIP-33) + properties: + hash: + type: string + description: Hash of referenced script + example: 67f33146617a5e61936081db3b2117cbf59bd2123748f58ac9678656 + size: + type: integer + description: Size in bytes + example: 14 + type: + type: string + description: Type of script + example: plutusV1 + bytes: + type: string + description: Script bytes (hex) + example: 4e4d01000033222220051200120011 + value: + type: object + nullable: true + description: Value (json) + example: null + asset_list: + type: array + nullable: true + description: An array of assets on the UTxO + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Quantity of assets on the UTxO + example: 1 + withdrawals: + type: array + description: Array of withdrawals with-in a transaction + nullable: true + items: + type: object + properties: + amount: + type: string + description: Withdrawal amount (in lovelaces) + example: 9845162 + stake_addr: + type: string + description: A Cardano staking address (reward account, bech32 encoded) + example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj + assets_minted: + type: array + description: Array of minted assets with-in a transaction + nullable: true + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + quantity: + type: string + description: Quantity of minted assets (negative on burn) + example: 1 + metadata: + $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + certificates: + type: array + nullable: true + description: Certificates present with-in a transaction (if any) + items: + properties: + index: + type: integer + nullable: true + description: Certificate index + example: 0 + type: + type: string + description: Type of certificate (could be delegation, stake_registration, stake_deregistraion, pool_update, pool_retire, param_proposal, reserve_MIR, treasury_MIR) + example: delegation + info: + type: object + description: A JSON array containing information from the certificate + nullable: true + example: + { + "stake_address": "stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj", + "pool": "pool1k53pf4wzn263c08e3wr3gttndfecm9f4uzekgctcx947vt7fh2p", + } + native_scripts: + type: array + nullable: true + description: Native scripts present in a transaction (if any) + items: + properties: + script_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + script_json: + type: object + description: JSON representation of the timelock script (null for other script types) + example: + { + "type": "all", + "scripts": + [ + { + "type": "sig", + "keyHash": "a96da581c39549aeda81f539ac3940ac0cb53657e774ca7e68f15ed9", + }, + { + "type": "sig", + "keyHash": "ccfcb3fed004562be1354c837a4a4b9f4b1c2b6705229efeedd12d4d", + }, + { + "type": "sig", + "keyHash": "74fcd61aecebe36aa6b6cd4314027282fa4b41c3ce8af17d9b77d0d1", + }, + ], + } + plutus_contracts: + type: array + description: Plutus contracts present in transaction (if any) + items: + properties: + address: + type: string + description: Plutus script address + example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 + script_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + bytecode: + type: string + description: CBOR-encoded Plutus script data + example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 + size: + type: integer + description: The size of the CBOR serialised script (in bytes) + example: 234895 + valid_contract: + type: boolean + description: True if the contract is valid or there is no contract + example: true + input: + type: object + properties: + redeemer: + type: object + properties: + purpose: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/purpose" + fee: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/fee" + unit: + type: object + properties: + steps: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/unit_steps" + mem: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/unit_mem" + datum: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + datum: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + output: + type: object + nullable: true + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + tx_utxos: + type: array + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + inputs: + $ref: "#/components/schemas/tx_info/items/properties/inputs" + outputs: + $ref: "#/components/schemas/tx_info/items/properties/outputs" + tx_metadata: + type: array + nullable: true + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + metadata: + type: object + nullable: true + description: A JSON array containing details about metadata within transaction + example: + { + "721": + { + "version": 1, + "copyright": "...", + "publisher": ["p...o"], + "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d": + {}, + }, + } + tx_status: + type: array + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + num_confirmations: + type: integer + description: Number of block confirmations + example: 17 + nullable: true + tx_metalabels: + type: array + items: + properties: + key: + type: string + description: A distinct known metalabel + example: "721" + asset_list: + type: array + description: Array of policy IDs and asset names + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + asset_address_list: + type: array + description: An array of payment addresses holding the given token (including balances) + items: + properties: + payment_address: + type: string + description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO + example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + quantity: + type: string + description: Asset balance on the payment address + example: 23 + asset_summary: + type: array + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_transactions: + type: integer + description: Total number of transactions including the given asset + example: 89416 + staked_wallets: + type: integer + description: Total number of registered wallets holding the given asset + example: 548 + unstaked_addresses: + type: integer + description: Total number of payment addresses (not belonging to registered wallets) holding the given asset + example: 245 + asset_info: + type: array + items: + properties: + policy_id: + type: string + description: Asset Policy ID (hex) + example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff + asset_name: + type: string + nullable: true + description: Asset Name (hex) + example: 444f4e545350414d + asset_name_ascii: + type: string + description: Asset Name (ASCII) + example: DONTSPAM + fingerprint: + type: string + description: The CIP14 fingerprint of the asset + example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 + minting_tx_hash: + type: string + description: Hash of the first mint transaction + example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 + mint_cnt: + type: integer + description: Count of total mint transactions + example: 1 + burn_cnt: + type: integer + description: Count of total burn transactions + example: 5 + minting_tx_metadata: + allOf: + - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" + description: Latest minting transaction metadata (aligns with CIP-25) + token_registry_metadata: + type: object + description: Asset metadata registered on the Cardano Token Registry + nullable: true + properties: + name: + type: string + example: Rackmob + description: + type: string + example: Metaverse Blockchain Cryptocurrency. + ticker: + type: string + example: MOB + url: + type: string + example: https://www.rackmob.com/ + logo: + type: string + description: A PNG image file as a byte string + example: iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADnmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc + decimals: + type: integer + example: 0 + total_supply: + type: string + example: "35000" + creation_time: + type: integer + description: UNIX timestamp of the first asset mint + example: 1506635091 + asset_history: + type: array + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_txs: + type: array + description: Array of all mint/burn transactions for an asset + nullable: true + items: + type: object + properties: + tx_hash: + type: string + description: Hash of minting/burning transaction + example: e1ecc517f95715bb87681cfde2c594dbc971739f84f8bfda16170b35d63d0ddf + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + quantity: + type: string + description: Quantity minted/burned (negative numbers indicate burn transactions) + example: "-10" + metadata: + type: array + description: Array of Transaction Metadata for given transaction + items: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + asset_policy_info: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + asset_name_ascii: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_tx_metadata: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + token_registry_metadata: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" + total_supply: + type: string + example: "35000" + creation_time: + type: string + $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_txs: + type: array + description: An array of Tx hashes that included the given asset (latest first) + items: + properties: + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" + native_script_list: + type: array + items: + properties: + script_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + creation_tx_hash: + $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" + type: + type: string + description: Type of the script + enum: ["timelock", "multisig"] + example: timelock + plutus_script_list: + type: array + items: + properties: + script_hash: + type: string + description: Hash of a script + example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 + creation_tx_hash: + type: string + description: Hash of the script creation transaction + example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + script_redeemers: + type: array + items: + type: object + properties: + script_hash: + type: string + description: Hash of Transaction for which details are being shown + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + redeemers: + type: array + items: + type: object + properties: + tx_hash: + type: string + description: Hash of Transaction containing the redeemer + example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + tx_index: + type: integer + description: The index of the redeemer pointer in the transaction + example: 0 + unit_mem: + description: The budget in Memory to run a script + example: 520448 + nullable: true + additionalProperties: + oneOf: + - type: string + - type: integer + unit_steps: + description: The budget in Cpu steps to run a script + example: 211535239 + nullable: true + additionalProperties: + oneOf: + - type: string + - type: integer + fee: + type: string + description: The budget in fees to run a script - the fees depend on the ExUnits and the current prices + example: 45282 + purpose: + type: string + description: What kind of validation this redeemer is used for + enum: ["spend", "mint", "cert", "reward"] + example: spend + datum_hash: + type: string + description: The Hash of the Plutus Data + nullable: true + example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 + datum_value: + type: object + description: The actual data in json format + example: { "bytes": "3c33" } + datum_info: + type: array + items: + type: object + properties: + hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + value: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + bytes: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" + headers: {} + responses: + OK: + description: Success! + NotFound: + description: The server does not recognise the combination of endpoint and parameters provided + Unauthorized: + description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint + PartialContent: + description: The result was truncated + BadRequest: + description: The server cannot process the request due to invalid input +tags: + - name: Network + description: Query information about the network + x-tag-expanded: false + - name: Epoch + description: Query epoch-specific details + x-tag-expanded: false + - name: Block + description: Query information about particular block on chain + x-tag-expanded: false + - name: Transactions + description: Query blockchain transaction details + x-tag-expanded: false + - name: Address + description: Query information about specific address(es) + x-tag-expanded: false + - name: Account + description: Query details about specific stake account addresses + x-tag-expanded: false + - name: Asset + description: Query Asset related informations + x-tag-expanded: false + - name: Pool + description: Query information about specific pools + x-tag-expanded: false + - name: Script + description: Query information about specific scripts (Smart Contracts) + x-tag-expanded: false +security: [] diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index ad59c3c9..a3a8a9d8 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.8 + version: 1.0.9rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -666,6 +666,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses (accounts) + /account_info_cached: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (accounts) /account_rewards: #RPC post: tags: @@ -910,6 +931,7 @@ paths: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": description: Array of Tx hashes that included the given asset @@ -923,8 +945,8 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transaction History - description: Get the list of all asset transaction hashes (newest first) + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) /pool_list: #RPC get: tags: @@ -1288,7 +1310,7 @@ components: description: Block height for specifying time delta schema: type: integer - example: 63487 + example: 50000 in: query required: false allowEmptyValue: true @@ -1368,10 +1390,20 @@ components: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: 54735465737431 + example: "54735465737431" in: query required: false allowEmptyValue: true + _history: + deprecated: false + name: _history + description: Include all historical transactions, setting to false includes only the non-empty ones + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: false _pool_bech32: deprecated: false name: _pool_bech32 @@ -2894,7 +2926,6 @@ components: } plutus_contracts: type: array - nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -2949,6 +2980,7 @@ components: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" output: type: object + nullable: true properties: hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" @@ -3148,7 +3180,10 @@ components: description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" metadata: - $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + type: array + description: Array of Transaction Metadata for given transaction + items: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index b0678013..7d3cd41d 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -1,6 +1,6 @@ info: title: Koios API - version: 1.0.8 + version: 1.0.9rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/templates/2-api-params.yaml b/specs/templates/2-api-params.yaml index f0d43844..2d75d18d 100644 --- a/specs/templates/2-api-params.yaml +++ b/specs/templates/2-api-params.yaml @@ -66,7 +66,7 @@ parameters: description: Block height for specifying time delta schema: type: integer - example: 63487 + example: 50000 in: query required: false allowEmptyValue: true @@ -146,10 +146,20 @@ parameters: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: ##_asset_name_param## + example: "##_asset_name_param##" in: query required: false allowEmptyValue: true + _history: + deprecated: false + name: _history + description: Include all historical transactions, setting to false includes only the non-empty ones + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: false _pool_bech32: deprecated: false name: _pool_bech32 diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 164e2a46..d7e070e7 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -1288,7 +1288,6 @@ schemas: } plutus_contracts: type: array - nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -1343,6 +1342,7 @@ schemas: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" output: type: object + nullable: true properties: hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" @@ -1542,7 +1542,10 @@ schemas: description: Quantity minted/burned (negative numbers indicate burn transactions) example: "-10" metadata: - $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" + type: array + description: Array of Transaction Metadata for given transaction + items: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" asset_policy_info: type: array description: List of policy assets diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index c5448a4f..95b83e8a 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -450,6 +450,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses (accounts) + /account_info_cached: #RPC + post: + tags: + - Account + requestBody: + $ref: "#/components/requestBodies/stake_addresses" + responses: + "200": + description: Array of account information + content: + application/json: + schema: + $ref: "#/components/schemas/account_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (accounts) /account_rewards: #RPC post: tags: @@ -694,6 +715,7 @@ paths: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": description: Array of Tx hashes that included the given asset @@ -707,8 +729,8 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transaction History - description: Get the list of all asset transaction hashes (newest first) + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) /pool_list: #RPC get: tags: diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index 4f92379d..b1fce30f 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -3,159 +3,221 @@ "_block_hash": { "m": "f6192a1aaa6d3d05b4703891a6b66cd757801c61ace86cbe5ab0d66e07f601ab", "t": "f75fea40852ed7d7f539d008e45255725daef8553ae7162750836f279570813a", - "g": "bddbbc6df0ad09567a513349bafd56d8ec5c8fcd9ee9db12173624b896350d57" + "g": "bddbbc6df0ad09567a513349bafd56d8ec5c8fcd9ee9db12173624b896350d57", + "pv": "6d6e3722c81a1b9bca9dd9b7f63cae096bd1f97f6ef1d67c178bacab1a248483", + "pp": "021486aaa71f3c20c9fb26864fc8b00f7966a9007ff21e0753f16b9532450aa8" }, "_epoch_no": { "m": "320", "t": "185", - "g": "1950" + "g": "1950", + "pv": "12", + "pp": "31" }, "_earned_epoch_no": { "m": "320", "t": "185", - "g": "1950" + "g": "1950", + "pv": "8", + "pp": "23" }, "_any_address": { "m": "stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz", "t": "addr_test1qqn5x72yymml6aka0cmkew3jynqgld7xlnwtlsen9ln5tfll0dw5r75vk42mv3ykq8vyjeaanvpytg79xqzymqy5acmqqhx2n7", - "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2" + "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2", + "pv": "stake_test1uqd2nz8ugrn6kwkflvmt9he8dr966dszfmm5lt66qdmn28qt4wff9", + "pp": "stake_test1uqc6cmh4um4t7x9p0nh5chlmf4txwhpw7572u8utuvgg80g47wc6x" }, "_address_assets": { "m": "addr1q8h22z0n3zqecr9n4q9ysds2m2ms3dqesz575accjpc3jclw55yl8zypnsxt82q2fqmq4k4hpz6pnq9fafm33yr3r93sgnpdw6", "t": "addr_test1qzd9gtea50mqv60k3mq9txxtq2ynqwsxcnlx9ltvv3lh0rk9q2x2l6wv8fcr4wpgwmrcwhucsp80ycfw5ensx038hlfsp6lsxj", - "g": "addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z" + "g": "addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z", + "pv": "addr_test1qphcrpt99pu2g2pwv32k8xqxa86pqswv2sa4tqefvruyfzeg3292mxuf3kq7nysjumlxjrlsfn9tp85r0l54l29x3qcsxxn580", + "pp": "addr_test1qps7qvkjm6dxa6pnd65surma3qkpng02cc0xfyh6zjksge343f8yzpwq3av7gauswp5ec7nj2e5fxve0nptaknn5904s7zmvuh" }, "_address": { "m": "addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g", "t": "addr_test1qpqd4nqjepzdlh9zx5mx5ftnp4hecpgttcprtg0ur3ptpe9efftg48dy58fqwqwvatkn6pj877x858cr7peyr9466jmshglmne", - "g": "addr_test1qqm3qvgq7wv7ywwgcwvwxscq3nclh06zwt3q09rhlzcdhx4lyk8g7246mrhwalx52nw0ntwcfml9wfgwkn22uqw82dcq7ct4gk" + "g": "addr_test1qqm3qvgq7wv7ywwgcwvwxscq3nclh06zwt3q09rhlzcdhx4lyk8g7246mrhwalx52nw0ntwcfml9wfgwkn22uqw82dcq7ct4gk", + "pv": "addr_test1qzrxlwtmf4zzqndw6trwlgmyhnk8hncdx9w6ls9vvgy4xa0am5at75hv9zravxzwf5wlalxqm4jlr6u8qkp4t2unmdpsjvpe2n", + "pp": "addr_test1qr33ynuc6yg4w56mccww3kcwqn54el84225xhvgkukf6wm32ah6gtt8ca4g0jdjcw50fk5ng5kpuc5uz4qula3gdlywssd3nq3" }, "_stake_address": { "m": "stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz", "t": "stake_test1uqqzl36c3vk850wk22yqgum9l0upy0y8588hcvsjq9m6j4cxw3qau", - "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2" + "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2", + "pv": "stake_test1uzs5rxys8qy5jnr9g0mkj860ms5n92nrykmrgyumpf2ytmsejj4m6", + "pp": "stake_test1urkzeud48zxwnjc54emzmmc3gkg2r6d6tm2sd799jxjnqxqlfzmvk" }, "_asset_policy": { "m": "750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501", "t": "000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b", - "g": "313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e" + "g": "313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e", + "pv": "065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404", + "pp": "c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e" }, "_asset_name": { "m": "424f4f4b", "t": "54735465737431", - "g": "41484c636f696e" + "g": "41484c636f696e", + "pv": "433374", + "pp": "7447454e53" }, "_pool_bech32": { "m": "pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc", "t": "pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh", - "g": "pool1xc9eywck4e20tydz4yvh5vfe0ep8whawvwz8wqkc9k046a2ypp4" + "g": "pool1xc9eywck4e20tydz4yvh5vfe0ep8whawvwz8wqkc9k046a2ypp4", + "pv": "pool1leml52hm4fcp3hhe4zye08qz27llhj7d339p3gs0tl85cstx59q", + "pp": "pool1tc3suyyxh9796rd92744mne82chxpmfc6mu7qgqzlx56sjfwvvl" }, "_pool_bech32_optional": { "m": "pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc", "t": "pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh", - "g": "pool1xc9eywck4e20tydz4yvh5vfe0ep8whawvwz8wqkc9k046a2ypp4" + "g": "pool1xc9eywck4e20tydz4yvh5vfe0ep8whawvwz8wqkc9k046a2ypp4", + "pv": "pool1leml52hm4fcp3hhe4zye08qz27llhj7d339p3gs0tl85cstx59q", + "pp": "pool1tc3suyyxh9796rd92744mne82chxpmfc6mu7qgqzlx56sjfwvvl" }, "_script_hash": { "m": "d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8", "t": "9a3910acc1e1d49a25eb5798d987739a63f65eb48a78462ffae21e6f", - "g": "160301a01ee86d8e46cbe3aef1e3bf69bfa28c65d5be2dde56a37af8" + "g": "160301a01ee86d8e46cbe3aef1e3bf69bfa28c65d5be2dde56a37af8", + "pv": "f758cf422ca0cbed7d9d6fad1eb5a3c70537d62e661ad450dd2a3810", + "pp": "590555d7b5760e98ae2bdd29b356247776251dfab0a207bfce98a485" } }, "requestBodies": { "epoch_no": { - "m": 350, - "t": 200, - "g": 1500 + "m": "350", + "t": "200", + "g": "1500", + "pv": "11", + "pp": "30" }, "stake_addresses1": { "m": "stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250", "t": "stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj", - "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2" + "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2", + "pv": "stake_test1upv7n2x0lxepkyx8ux2gjt74ecaa39tjgaccxl6hw5fwzngpzf5zt", + "pp": "stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l" }, "stake_addresses2": { "m": "stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy", "t": "stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx", - "g": "stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd" + "g": "stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd", + "pv": "stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl", + "pp": "stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx" }, "payment_addresses1": { - "m": "addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g", + "m": "addr1qy2jt0qpqz2z2z9zx5w4xemekkce7yderz53kjue53lpqv90lkfa9sgrfjuz6uvt4uqtrqhl2kj0a9lnr9ndzutx32gqleeckv", "t": "addr_test1qzx9hu8j4ah3auytk0mwcupd69hpc52t0cw39a65ndrah86djs784u92a3m5w475w3w35tyd6v3qumkze80j8a6h5tuqq5xe8y", - "g": "addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z" + "g": "addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z", + "pv": "addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc", + "pp": "addr_test1vzpwq95z3xyum8vqndgdd9mdnmafh3djcxnc6jemlgdmswcve6tkw" }, "payment_addresses2": { - "m": "addr1qyfldpcvte8nkfpyv0jdc8e026cz5qedx7tajvupdu2724tlj8sypsq6p90hl40ya97xamkm9fwsppus2ru8zf6j8g9sm578cu", + "m": "addr1q9xvgr4ehvu5k5tmaly7ugpnvekpqvnxj8xy50pa7kyetlnhel389pa4rnq6fmkzwsaynmw0mnldhlmchn2sfd589fgsz9dd0y", "t": "addr_test1qrk7920v35zukhcch4kyydy6rxnhqdcvetkvngeqrvtgavw8tpzdklse3kwer7urhrlfg962m9fc8cznfcdpka5pd07sgf8n0w", - "g": "addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu" + "g": "addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu", + "pv": "addr_test1vqneq3v0dqh3x3muv6ee3lt8e5729xymnxuavx6tndcjc2cv24ef9", + "pp": "addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc" }, "address_txs_after_block_height": { "m": "6238675", "t": "2342661", - "g": "120000" + "g": "120000", + "pv": "40356", + "pp": "9417" }, "block_info1": { "m": "fb9087c9f1408a7bbd7b022fd294ab565fec8dd3a8ef091567482722a1fa4e30", "t": "f75fea40852ed7d7f539d008e45255725daef8553ae7162750836f279570813a", - "g": "af2f6f7dd4e4ea6765103a1e38e023da3edd2b3c7fea2aa367222564dbe01cfd" + "g": "af2f6f7dd4e4ea6765103a1e38e023da3edd2b3c7fea2aa367222564dbe01cfd", + "pv": "a4504e2495ed03b48be36676f430c54dca0769d29f72ebf18d493abf42d2167b", + "pp": "2abeb8d1c1227139763be30ddb7a2fd79abd7d44195fca87a7c836a510b2802d" }, "block_info2": { "m": "60188a8dcb6db0d80628815be2cf626c4d17cb3e826cebfca84adaff93ad492a", "t": "ff9f0c7fb1136de2cd6f10c9a140af2887f1d3614cc949bfeb262266d4c202b7", - "g": "bddbbc6df0ad09567a513349bafd56d8ec5c8fcd9ee9db12173624b896350d57" + "g": "bddbbc6df0ad09567a513349bafd56d8ec5c8fcd9ee9db12173624b896350d57", + "pv": "8e7a6206d2b21ae4f26e7e09353fadae17f838a63d095c2be51acbd16e9b7716", + "pp": "4e790b758c495953bb33c4aad4a4b4c1b98f7c2ec135ebd3db21f32059481718" }, "block_info3": { "m": "c6646214a1f377aa461a0163c213fc6b86a559a2d6ebd647d54c4eb00aaab015", "t": "5ef645ee519cde94a82f0aa880048c37978374f248f11e408ac0571a9054d9d3", - "g": "732bf9bbc3780e6cd7ad57a3889dd5904f6e8a27d54eabf43eb0dbc485323f04" + "g": "732bf9bbc3780e6cd7ad57a3889dd5904f6e8a27d54eabf43eb0dbc485323f04", + "pv": "1baaf7812ed48e663adb9eeaa68fe25034e5e30b4f8e56cc8600cac5e9d42ce7", + "pp": "389da613316d2aec61edc34d51f1b3d004891ab38c9419771e5e0a3b12de3ef6" }, "credential_txs_payment_credentials1": { "m": "025b0a8f85cb8a46e1dda3fae5d22f07e2d56abb4019a2129c5d6c52", "t": "00003fac863dc2267d0cd90768c4af653572d719a79ca3b01957fa79", - "g": "b6b4b2b1e9e78369992aa5dd108aa4c3328fa228794ea9693400c2a0" + "g": "b6b4b2b1e9e78369992aa5dd108aa4c3328fa228794ea9693400c2a0", + "pv": "33c378cee41b2e15ac848f7f6f1d2f78155ab12d93b713de898d855f", + "pp": "b429738bd6cc58b5c7932d001aa2bd05cfea47020a556c8c753d4436" }, "credential_txs_payment_credentials2": { "m": "13f6870c5e4f3b242463e4dc1f2f56b02a032d3797d933816f15e555", "t": "000056d48603bf7daada30c9c175be9c93172d36f82fba0ca972c245", - "g": "35e45387fc2acdd5cd641e339990e665ad4d9d065a41c94bc0b4be13" + "g": "35e45387fc2acdd5cd641e339990e665ad4d9d065a41c94bc0b4be13", + "pv": "52e63f22c5107ed776b70f7b92248b02552fd08f3e747bc745099441", + "pp": "82e016828989cd9d809b50d6976d9efa9bc5b2c1a78d4b3bfa1bb83b" }, "tx_ids_tx_hashes1": { "m": "f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e", "t": "928052b80bfc23801da525a6bf8f805da36f22fa0fd5fec2198b0746eb82b72b", - "g": "bf04578d452dd3acb7c70fbac32dc972cb69f932f804171cfb4268f5af0228e7" + "g": "bf04578d452dd3acb7c70fbac32dc972cb69f932f804171cfb4268f5af0228e7", + "pv": "f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c", + "pp": "d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530" }, "tx_ids_tx_hashes2": { "m": "0b8ba3bed976fa4913f19adc9f6dd9063138db5b4dd29cecde369456b5155e94", "t": "c7e96e4cd6aa9e3afbc7b32d1e8023daf4197931f1ea61d2bdfc7a2e5e017cf1", - "g": "63b716064012f858450731cb5f960c100c6cb639ec1ec999b898c604451f116a" + "g": "63b716064012f858450731cb5f960c100c6cb639ec1ec999b898c604451f116a", + "pv": "206f6da5b0b0de45605a95f5ce7e172be9674550f7dde3838c45cbf24bab8b00", + "pp": "145688d3619e7524510ea64c0ec6363b77a9b8da179ef9bb0273a0940d57d576" }, "txbin": { "m": "f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e", "t": "928052b80bfc23801da525a6bf8f805da36f22fa0fd5fec2198b0746eb82b72b", - "g": "bf04578d452dd3acb7c70fbac32dc972cb69f932f804171cfb4268f5af0228e7" + "g": "bf04578d452dd3acb7c70fbac32dc972cb69f932f804171cfb4268f5af0228e7", + "pv": "f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c", + "pp": "d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530" }, "pool_ids_pool_bech32_ids1": { "m": "pool100wj94uzf54vup2hdzk0afng4dhjaqggt7j434mtgm8v2gfvfgp", "t": "pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh", - "g": "pool19st4a2vvu78tjtyywjte2eml3kx6ynersgd2nyw0y4jvyhlfu0u" + "g": "pool19st4a2vvu78tjtyywjte2eml3kx6ynersgd2nyw0y4jvyhlfu0u", + "pv": "pool1p90428kec03mjdya3k4gv5d20w7lmed7ca0snknef5j977l3y8l", + "pp": "pool1ext7qrwjzaxcdfhdnkq5mth59ukuu2atcg6tgqpmevpt7ratkta" }, "pool_ids_pool_bech32_ids2": { "m": "pool102s2nqtea2hf5q0s4amj0evysmfnhrn4apyyhd4azcmsclzm96m", "t": "pool102x86jz7uus6p6mlw02fdw2s805kng7g6ujs6s342t5msk36tch", - "g": "pool1uul8pytp2p0xq4ckjn3l294km0m7fuef46teehvh3x5tk46sfx3" + "g": "pool1uul8pytp2p0xq4ckjn3l294km0m7fuef46teehvh3x5tk46sfx3", + "pv": "pool1wwh3k3ldzujdvgxllfwlnnkxyheafkacqlufnvpr77n5q72f9hw", + "pp": "pool1x4p3cwemsm356vpxnjwuud7w76jz64hyss729zp7xa6wuey6yr9" }, "pool_ids_pool_bech32_ids3": { "m": "pool102vsulhfx8ua2j9fwl2u7gv57fhhutc3tp6juzaefgrn7ae35wm", "t": "pool103qt58f9xlsr7y9anz3lnyq6cph4xh2yr4qrrtc356ldzz6ktqz", - "g": "pool1us9ww725p0vygae5zaydme3apt7wg9se2yemhl8mgwkes3tlrqp" + "g": "pool1us9ww725p0vygae5zaydme3apt7wg9se2yemhl8mgwkes3tlrqp", + "pv": "pool1p835jxsj8py5n34lrgk6fvpgpxxvh585qm8dzvp7ups37vdet5a", + "pp": "pool1ws42l6rawqjv58crs5l32v0eem3qnngpnjfd7epwd4lmjccc5cg" }, "datum_hashes1": { "m": "818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46", "t": "5865d96e4313780a37af26055cdcc90308db23adaf4f2082178355e5e1dc20f6", - "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0" + "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", + "pv": "6181b3dc623cd8812caf027a3507e9b3095388a7cf3db65983e1fddd3a84c88c", + "pp": "5571e2c3549f15934a38382d1318707a86751fb70827f4cbd29b104480f1be9b" }, "datum_hashes2": { "m": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", "t": "4932dce28712ccc4858e3d83cc8e79b12740f66007dc9a287bb640a264c899de", - "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0" + "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", + "pv": "f8ae55ff89e1f5366f23e16bcaf2073252337b96031a02d79e41d653b5f0a978", + "pp": "5f7212f546d7e7308ce99b925f05538db19981f4ea3084559c0b28a363245826" } } } diff --git a/tests/setup-tests.sh b/tests/setup-tests.sh index 428d6485..1262ac58 100755 --- a/tests/setup-tests.sh +++ b/tests/setup-tests.sh @@ -12,8 +12,8 @@ cat <<-EOF To run the endpoint validation tests, use the below: - schemathesis --pre-run not_empty_response run --request-timeout 5000 --hypothesis-seed 1 https://guild.koios.rest/koiosapi.yaml \\ - --hypothesis-phases=explicit,generate --hypothesis-max-examples=1 -v --hypothesis-verbosity quiet -b http://127.0.0.1:8053/api/v0 -c all + schemathesis --pre-run not_empty_response run --request-timeout 5000 https://guild.koios.rest/koiosapi.yaml --hypothesis-phases=explicit \\ + --hypothesis-verbosity quiet -b http://127.0.0.1:8053/api/v0 -c all --validate-schema=true -H "Content-Type: application/json" where http://127.0.0.1:8053/api/v0 is the URL of instance you want to test, and guild.koios.rest is the target enviornment for testing. diff --git a/topology/topology-preprod.json b/topology/topology-preprod.json new file mode 100644 index 00000000..f9e3fda2 --- /dev/null +++ b/topology/topology-preprod.json @@ -0,0 +1,8 @@ +{ + "Providers": [ + {"name":"rdlrt","addr":"209.145.50.190","port":18050}, + {"name":"eden","addr":"eden-preprod.koios.rest","port":8053}, + {"name":"ola-ssl","addr":"koios-preprod.ahlnet.nu","port":2155,"ssl":"true"} + ], + "Consumers": [] +} diff --git a/topology/topology-preview.json b/topology/topology-preview.json new file mode 100644 index 00000000..b71c78cc --- /dev/null +++ b/topology/topology-preview.json @@ -0,0 +1,8 @@ +{ + "Providers": [ + {"name":"rdlrt","addr":"194.36.145.157","port":28050}, + {"name":"damjan","addr":"195.201.129.190","port":8053}, + {"name":"ola-ssl","addr":"koios-preview.ahlnet.nu","port":2175,"ssl":"true"} + ], + "Consumers": [] +} From da21835237946f167b502ad4f83fd02e7e71a8d9 Mon Sep 17 00:00:00 2001 From: KoT_B_KocMoce <49576827+hodlonaut@users.noreply.github.com> Date: Tue, 8 Nov 2022 21:30:31 +1100 Subject: [PATCH 23/86] Added homer to preprod topology (#126) --- topology/topology-preprod.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/topology/topology-preprod.json b/topology/topology-preprod.json index f9e3fda2..d6ae6c09 100644 --- a/topology/topology-preprod.json +++ b/topology/topology-preprod.json @@ -2,7 +2,8 @@ "Providers": [ {"name":"rdlrt","addr":"209.145.50.190","port":18050}, {"name":"eden","addr":"eden-preprod.koios.rest","port":8053}, - {"name":"ola-ssl","addr":"koios-preprod.ahlnet.nu","port":2155,"ssl":"true"} + {"name":"ola-ssl","addr":"koios-preprod.ahlnet.nu","port":2155,"ssl":"true"}, + {"name":"homer","addr":"154.12.248.114","port":8053} ], "Consumers": [] } From 5d0d6eb2eb455081ddae0b7b8200bd676ef8eea4 Mon Sep 17 00:00:00 2001 From: chadle-git <60274638+chadle-git@users.noreply.github.com> Date: Tue, 8 Nov 2022 04:32:47 -0600 Subject: [PATCH 24/86] Add BBHMM for preview net node (#125) * Update topology-preview.json Co-authored-by: chadle-git Co-authored-by: RdLrT <3169068+rdlrt@users.noreply.github.com> --- topology/topology-preview.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/topology/topology-preview.json b/topology/topology-preview.json index b71c78cc..91344cba 100644 --- a/topology/topology-preview.json +++ b/topology/topology-preview.json @@ -2,7 +2,8 @@ "Providers": [ {"name":"rdlrt","addr":"194.36.145.157","port":28050}, {"name":"damjan","addr":"195.201.129.190","port":8053}, - {"name":"ola-ssl","addr":"koios-preview.ahlnet.nu","port":2175,"ssl":"true"} + {"name":"ola-ssl","addr":"koios-preview.ahlnet.nu","port":2175,"ssl":"true"}, + {"name":"bbhmm","addr":"74.122.122.121","port":8053} ], "Consumers": [] } From 7017696b54b37d1697a70847894d03174a15ef23 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Mon, 14 Nov 2022 19:09:02 +1100 Subject: [PATCH 25/86] Update dashboards to cater for 2 haproxy instances (#133) * Update dashboards to cater for 2 haproxy instances * Add Huth's instances, supercedes #128,#129,#130,#131,#132 --- .../{dbsync.json => guild.json} | 350 +++++----- grafana-dashboards/koios-servers.json | 625 ++++++++---------- topology/topology-guild.json | 2 +- topology/topology-mainnet.json | 2 +- topology/topology-preprod.json | 3 +- topology/topology-preview.json | 3 +- topology/topology-testnet.json | 1 - 7 files changed, 464 insertions(+), 522 deletions(-) rename grafana-dashboards/{dbsync.json => guild.json} (81%) diff --git a/grafana-dashboards/dbsync.json b/grafana-dashboards/guild.json similarity index 81% rename from grafana-dashboards/dbsync.json rename to grafana-dashboards/guild.json index 2b500311..74eed87f 100644 --- a/grafana-dashboards/dbsync.json +++ b/grafana-dashboards/guild.json @@ -3,20 +3,29 @@ "list": [ { "builtIn": 1, - "datasource": "-- Grafana --", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, "type": "dashboard" } ] }, "description": "Stake Pool Monitor", "editable": true, - "gnetId": null, + "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 11, + "iteration": 1668403650564, "links": [ { "icon": "external link", @@ -26,14 +35,17 @@ "url": "https://cardano-community.github.io/guild-operators" } ], + "liveNow": false, "panels": [ { "aliasColors": {}, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "decimals": 0, "description": "", "fieldConfig": { @@ -73,7 +85,7 @@ "alertThreshold": false }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": false, "renderer": "flot", @@ -83,19 +95,22 @@ "steppedLine": false, "targets": [ { - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "haproxy_server_check_status{state=\"PROCOK\",proxy=\"grest_core\"}", + "expr": "haproxy_server_check_status{state=\"PROCOK\",proxy=\"grest_core\",server=~\"$gRestNode\",instance=\"us-api.koios.rest:443\"}", "format": "time_series", "interval": "", "legendFormat": "{{ server }}", + "range": true, "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Servers State", "tooltip": { "shared": true, @@ -104,9 +119,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -116,7 +129,6 @@ "format": "locale", "label": "", "logBase": 1, - "max": null, "min": "0", "show": true }, @@ -125,8 +137,6 @@ "format": "short", "label": "Relays", "logBase": 1, - "max": null, - "min": null, "show": false } ], @@ -138,10 +148,12 @@ { "aliasColors": {}, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "decimals": 0, "description": "", "fieldConfig": { @@ -181,7 +193,7 @@ "alertThreshold": false }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -191,19 +203,22 @@ "steppedLine": false, "targets": [ { - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "dbsize/1024/1024/1024 ", + "expr": "dbsize{service=~\"$gRestNode\"}/1024/1024/1024", "format": "time_series", "interval": "", "legendFormat": "{{ service }}", + "range": true, "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "DB Size (GB)", "tooltip": { "shared": true, @@ -212,9 +227,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -224,8 +237,6 @@ "format": "locale", "label": "", "logBase": 1, - "max": null, - "min": null, "show": true }, { @@ -233,14 +244,11 @@ "format": "short", "label": "Relays", "logBase": 1, - "max": null, - "min": null, "show": false } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -248,7 +256,10 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "description": "", "fieldConfig": { "defaults": { @@ -284,7 +295,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -298,8 +309,12 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "(memused / memtotal)* 100", + "expr": "(memused{service=~\"$gRestNode\"} / memtotal{service=~\"$gRestNode\"})* 100", "hide": false, "interval": "", "legendFormat": "{{ service }}", @@ -307,9 +322,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Memory Utilization", "tooltip": { "shared": true, @@ -318,9 +331,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -328,33 +339,28 @@ { "decimals": 1, "format": "%", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { "aliasColors": {}, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "decimals": 0, "description": "", "fieldConfig": { @@ -394,7 +400,7 @@ "alertThreshold": false }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -404,18 +410,19 @@ "steppedLine": false, "targets": [ { - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "nodeBlockHeight", + "expr": "nodeBlockHeight{service=~\"$gRestNode\"}", "interval": "", "legendFormat": "{{ service }}", "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Block height (node)", "tooltip": { "shared": true, @@ -424,9 +431,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -435,31 +440,28 @@ "format": "locale", "label": "", "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", "label": "Relays", "logBase": 1, - "max": null, - "min": null, "show": false } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { "aliasColors": {}, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "decimals": 0, "description": "", "fieldConfig": { @@ -499,7 +501,7 @@ "alertThreshold": false }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -509,18 +511,19 @@ "steppedLine": false, "targets": [ { - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "dbsyncBlockHeight", + "expr": "dbsyncBlockHeight{service=~\"$gRestNode\"}", "interval": "", "legendFormat": "{{ service }}", "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Block height (dbsync)", "tooltip": { "shared": true, @@ -529,9 +532,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -541,8 +542,6 @@ "format": "locale", "label": "", "logBase": 1, - "max": null, - "min": null, "show": true }, { @@ -550,23 +549,22 @@ "format": "short", "label": "Relays", "logBase": 1, - "max": null, - "min": null, "show": false } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { "aliasColors": {}, "bars": false, - "cacheTimeout": null, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "decimals": 0, "description": "", "fieldConfig": { @@ -607,7 +605,7 @@ "alertThreshold": false }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -617,9 +615,12 @@ "steppedLine": false, "targets": [ { - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "dbsyncQueueLength", + "expr": "dbsyncQueueLength{service=~\"$gRestNode\"}", "format": "time_series", "instant": false, "interval": "", @@ -628,9 +629,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Queue Length (dbsync)", "tooltip": { "shared": true, @@ -639,9 +638,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -651,7 +648,6 @@ "format": "locale", "label": "", "logBase": 1, - "max": null, "min": "0", "show": true }, @@ -660,14 +656,11 @@ "format": "short", "label": "Relays", "logBase": 1, - "max": null, - "min": null, "show": false } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -675,7 +668,10 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "fieldConfig": { "defaults": { "links": [] @@ -711,19 +707,22 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", - "repeat": null, "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "(load1m/100)", + "expr": "(load1m{service=~\"$gRestNode\"}/100)", "format": "time_series", "hide": false, "interval": "", @@ -734,9 +733,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Load 1m", "tooltip": { "shared": true, @@ -745,9 +742,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -758,23 +753,18 @@ "format": "none", "label": "Load", "logBase": 1, - "max": null, "min": "0", "show": true }, { "$$hashKey": "object:316", "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": false } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -782,12 +772,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "unit": "s" - }, - "overrides": [] + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" }, "fill": 0, "fillGradient": 0, @@ -817,7 +804,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -829,8 +816,12 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "abs(nodetipref)", + "expr": "abs(nodetipref{service=~\"$gRestNode\"})", "format": "time_series", "interval": "", "legendFormat": "{{ service }}", @@ -838,9 +829,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Tipp diff (node)", "tooltip": { "shared": true, @@ -849,33 +838,26 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { + "$$hashKey": "object:133", "format": "s", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { + "$$hashKey": "object:134", "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -883,7 +865,10 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "fieldConfig": { "defaults": { "unit": "s" @@ -918,7 +903,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 1, "points": true, "renderer": "flot", @@ -932,8 +917,12 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "dbsynctipref", + "expr": "dbsynctipref{service=~\"$gRestNode\"}", "format": "time_series", "interval": "", "legendFormat": "{{ service }}", @@ -941,9 +930,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Tipp diff (dbsync)", "tooltip": { "shared": true, @@ -952,9 +939,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -962,25 +947,19 @@ { "$$hashKey": "object:363", "format": "s", - "label": null, "logBase": 1, - "max": null, - "min": null, + "min": "0", "show": true }, { "$$hashKey": "object:364", "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -988,7 +967,10 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "description": "", "fieldConfig": { "defaults": { @@ -1024,7 +1006,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -1034,8 +1016,12 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "abs(cpuutil)", + "expr": "abs(cpuutil{service=~\"$gRestNode\"})", "hide": false, "interval": "", "legendFormat": "{{ service }}", @@ -1043,9 +1029,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "CPU Utilization", "tooltip": { "shared": true, @@ -1054,9 +1038,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1065,25 +1047,18 @@ "$$hashKey": "object:102", "decimals": 1, "format": "%", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "$$hashKey": "object:103", "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1091,10 +1066,9 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": {}, - "overrides": [] + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" }, "fill": 0, "fillGradient": 0, @@ -1122,7 +1096,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 0.5, "points": true, "renderer": "flot", @@ -1132,17 +1106,19 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "exemplar": true, - "expr": "uptime", + "expr": "uptime{service=~\"$gRestNode\"}", "interval": "", "legendFormat": "{{ service }}", "refId": "A" } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Uptime (node)", "tooltip": { "shared": true, @@ -1151,45 +1127,68 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "s", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true }, { "format": "short", - "label": null, "logBase": 1, - "max": null, - "min": null, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], "refresh": "30s", - "schemaVersion": 27, + "schemaVersion": 36, "style": "dark", "tags": [], "templating": { - "list": [] + "list": [ + { + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "definition": "label_values(service)", + "hide": 0, + "includeAll": true, + "label": "gRestNode", + "multi": true, + "name": "gRestNode", + "options": [], + "query": { + "query": "label_values(service)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] }, "time": { - "from": "now-6h", + "from": "now-3h", "to": "now" }, "timepicker": { @@ -1219,5 +1218,6 @@ "timezone": "", "title": "GRest Operator's monitoring", "uid": "GRest", - "version": 1 + "version": 84, + "weekStart": "" } diff --git a/grafana-dashboards/koios-servers.json b/grafana-dashboards/koios-servers.json index e2193e01..933b8566 100644 --- a/grafana-dashboards/koios-servers.json +++ b/grafana-dashboards/koios-servers.json @@ -3,26 +3,39 @@ "list": [ { "builtIn": 1, - "datasource": "-- Grafana --", + "datasource": { + "type": "datasource", + "uid": "grafana" + }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, "type": "dashboard" } ] }, "description": "HAproxy backend servers", "editable": true, + "fiscalYearStartMonth": 0, "gnetId": 367, "graphTooltip": 1, - "id": 4, - "iteration": 1646778315151, + "iteration": 1668399276243, "links": [], + "liveNow": false, "panels": [ { "collapsed": false, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "gridPos": { "h": 1, "w": 24, @@ -31,7 +44,6 @@ }, "id": 29, "panels": [], - "repeat": null, "title": "Server Status", "type": "row" }, @@ -40,14 +52,13 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "decimals": 0, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 5, "fillGradient": 0, "grid": {}, @@ -78,7 +89,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -88,20 +99,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "haproxy_server_check_status{state=\"PROCOK\",proxy=\"grest_core\"}", + "expr": "haproxy_server_check_status{state=\"PROCOK\",proxy=\"grest_core\",instance=~\"$instance\"}", "format": "time_series", "interval": "", "intervalFactor": 2, "legendFormat": "{{ server }}", + "range": true, "refId": "A", "step": 30 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Servers UP (stacked)", "tooltip": { "msResolution": true, @@ -111,9 +126,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -121,25 +134,20 @@ { "$$hashKey": "object:117", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:118", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -147,13 +155,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 5, "fillGradient": 0, "grid": {}, @@ -184,7 +191,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -194,20 +201,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "haproxy_server_check_status{state=\"PROCERR\",proxy=\"grest_core\"}", + "expr": "haproxy_server_check_status{state=\"PROCERR\",proxy=\"grest_core\",instance=~\"$instance\"}", "interval": "$interval", "intervalFactor": 2, "legendFormat": "{{ server }}", "metric": "haproxy_backend_current_queue", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Servers Down (Stacked)", "tooltip": { "msResolution": true, @@ -217,9 +228,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -227,134 +236,124 @@ { "$$hashKey": "object:1530", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:1531", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "prometheus", - "editable": true, - "error": false, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "fieldConfig": { - "defaults": {}, + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 50, + "gradientMode": "opacity", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "stepAfter", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, "overrides": [] }, - "fill": 5, - "fillGradient": 5, - "grid": {}, "gridPos": { "h": 7, "w": 8, "x": 16, "y": 1 }, - "hiddenSeries": false, "id": 23, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 2, "links": [], - "nullPointMode": "connected", "options": { - "alertThreshold": true + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } }, - "percentage": false, - "pluginVersion": "7.5.7", - "pointradius": 5, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": true, + "pluginVersion": "9.0.0", "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, "expr": "haproxy_backend_status{proxy=\"grest_core\",state=\"UP\"}", "interval": "", "intervalFactor": 2, - "legendFormat": "{{ $proxy }}", + "legendFormat": "{{instance}}", + "range": true, "refId": "A", "step": 30 } ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, "title": "Proxy Status", - "tooltip": { - "msResolution": true, - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:170", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:171", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } + "type": "timeseries" }, { "collapsed": false, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "gridPos": { "h": 1, "w": 24, @@ -363,7 +362,6 @@ }, "id": 26, "panels": [], - "repeat": null, "title": "Connections", "type": "row" }, @@ -372,13 +370,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 5, "grid": {}, @@ -409,7 +406,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -424,20 +421,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_connection_attempts_total{backend=~\"$backend\", server=~\"$server\", proxy=\"grest_core\", alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_connection_attempts_total{proxy=~\"$backend\", server=~\"$server\", proxy=\"grest_core\", instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backe", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "New Connections Rate", "tooltip": { "msResolution": true, @@ -447,9 +448,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -457,25 +456,20 @@ { "$$hashKey": "object:1474", "format": "none", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:1475", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -483,13 +477,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 5, "grid": {}, @@ -519,7 +512,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -530,7 +523,7 @@ "targets": [ { "exemplar": true, - "expr": "irate(haproxy_server_sessions_total{backend=~\"$backend\",proxy=\"grest_core\" , server=~\"$server\", alias=\"$alias\"}[360s])", + "expr": "irate(haproxy_server_sessions_total{proxy=~\"$backend\",proxy=\"grest_core\" , server=~\"$server\", instance=~\"$instance\"}[360s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", @@ -540,9 +533,7 @@ } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "New Session Rate", "tooltip": { "msResolution": true, @@ -552,9 +543,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -562,25 +551,20 @@ { "$$hashKey": "object:223", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:224", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -588,13 +572,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 5, "grid": {}, @@ -625,7 +608,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -640,20 +623,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "haproxy_server_connection_attempts_total{backend=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", alias=\"$alias\"}", + "expr": "haproxy_server_connection_attempts_total{proxy=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", instance=~\"$instance\"}", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backe", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Total Connections (volatile)", "tooltip": { "msResolution": true, @@ -663,9 +650,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -673,30 +658,28 @@ { "$$hashKey": "object:1474", "format": "none", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:1475", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { "collapsed": false, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "gridPos": { "h": 1, "w": 24, @@ -705,7 +688,6 @@ }, "id": 25, "panels": [], - "repeat": null, "title": "Throughput", "type": "row" }, @@ -714,13 +696,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 5, "grid": {}, @@ -751,7 +732,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -761,20 +742,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_bytes_in_total{backend=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_bytes_in_total{proxy=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backend_", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Bytes IN", "tooltip": { "msResolution": true, @@ -784,9 +769,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -794,25 +777,20 @@ { "$$hashKey": "object:407", "format": "bytes", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:408", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -820,13 +798,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 5, "grid": {}, @@ -857,7 +834,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -867,20 +844,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_bytes_out_total{backend=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_bytes_out_total{proxy=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backend_", + "range": true, "refId": "B", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Bytes OUT", "tooltip": { "msResolution": true, @@ -890,9 +871,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -900,30 +879,28 @@ { "$$hashKey": "object:502", "format": "bytes", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:503", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { "collapsed": false, - "datasource": null, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "gridPos": { "h": 1, "w": 24, @@ -932,7 +909,6 @@ }, "id": 24, "panels": [], - "repeat": null, "title": "Backend Responses", "type": "row" }, @@ -941,13 +917,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 5, "grid": {}, @@ -978,7 +953,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -988,20 +963,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_http_responses_total{backend=~\"$backend\", server=~\"$server\", code=~\"2xx\",proxy=\"grest_core\" , alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_http_responses_total{proxy=~\"$backend\", server=~\"$server\", code=~\"2xx\",proxy=\"grest_core\" , instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backend_current_queue", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "HTTP Responses [2xx]", "tooltip": { "msResolution": true, @@ -1011,9 +990,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1021,25 +998,20 @@ { "$$hashKey": "object:555", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:556", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1047,13 +1019,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "grid": {}, @@ -1084,7 +1055,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -1094,20 +1065,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_http_responses_total{backend=~\"$backend\", server=~\"$server\", code=~\"4xx\",proxy=\"grest_core\", alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_http_responses_total{proxy=~\"$backend\", server=~\"$server\", code=~\"4xx\",proxy=\"grest_core\", instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backend_current_queue", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "HTTP Responses [4xx]", "tooltip": { "msResolution": true, @@ -1117,9 +1092,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1127,25 +1100,20 @@ { "$$hashKey": "object:608", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:609", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1153,13 +1121,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "grid": {}, @@ -1190,7 +1157,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -1200,20 +1167,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_http_responses_total{backend=~\"$backend\", server=~\"$server\", code=~\"5xx\",proxy=\"grest_core\" , alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_http_responses_total{proxy=~\"$backend\", server=~\"$server\", code=~\"5xx\",proxy=\"grest_core\" , instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backend_current_queue", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "HTTP Responses [5xx]", "tooltip": { "msResolution": true, @@ -1223,9 +1194,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1233,25 +1202,20 @@ { "$$hashKey": "object:1254", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:1255", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1259,13 +1223,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 0, "fillGradient": 0, "grid": {}, @@ -1296,7 +1259,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -1311,20 +1274,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_connection_errors_total{backend=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_connection_errors_total{proxy=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backe", + "range": true, "refId": "B", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Connection Errors Rate", "tooltip": { "msResolution": true, @@ -1334,9 +1301,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1344,25 +1309,20 @@ { "$$hashKey": "object:711", "format": "none", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:712", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1370,13 +1330,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "grid": {}, @@ -1407,7 +1366,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -1417,20 +1376,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_response_errors_total{backend=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_response_errors_total{proxy=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backend_current_queue", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Response Errors", "tooltip": { "msResolution": true, @@ -1440,9 +1403,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1450,25 +1411,20 @@ { "$$hashKey": "object:764", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:765", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } }, { @@ -1476,13 +1432,12 @@ "bars": false, "dashLength": 10, "dashes": false, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "editable": true, "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, "fill": 1, "fillGradient": 0, "grid": {}, @@ -1513,7 +1468,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.7", + "pluginVersion": "9.0.0", "pointradius": 5, "points": false, "renderer": "flot", @@ -1523,20 +1478,24 @@ "steppedLine": false, "targets": [ { + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "editorMode": "code", "exemplar": true, - "expr": "irate(haproxy_server_check_failures_total{backend=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", alias=\"$alias\"}[180s])", + "expr": "irate(haproxy_server_check_failures_total{proxy=~\"$backend\", server=~\"$server\",proxy=\"grest_core\", instance=~\"$instance\"}[180s])", "interval": "$interval", "intervalFactor": 1, "legendFormat": "{{ server }}", "metric": "haproxy_backend_current_queue", + "range": true, "refId": "A", "step": 60 } ], "thresholds": [], - "timeFrom": null, "timeRegions": [], - "timeShift": null, "title": "Check Failures", "tooltip": { "msResolution": true, @@ -1546,9 +1505,7 @@ }, "type": "graph", "xaxis": { - "buckets": null, "mode": "time", - "name": null, "show": true, "values": [] }, @@ -1556,30 +1513,25 @@ { "$$hashKey": "object:817", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true }, { "$$hashKey": "object:818", "format": "short", - "label": null, "logBase": 1, - "max": null, "min": 0, "show": true } ], "yaxis": { - "align": false, - "alignLevel": null + "align": false } } ], "refresh": "1m", - "schemaVersion": 27, + "schemaVersion": 36, "style": "dark", "tags": [ "haproxy", @@ -1596,9 +1548,6 @@ "text": "1m", "value": "1m" }, - "datasource": null, - "description": null, - "error": null, "hide": 0, "includeAll": false, "label": "Interval", @@ -1643,7 +1592,6 @@ "type": "interval" }, { - "allValue": null, "current": { "selected": true, "text": [ @@ -1653,10 +1601,11 @@ "$__all" ] }, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "definition": "", - "description": null, - "error": null, "hide": 0, "includeAll": true, "label": "Backend", @@ -1664,53 +1613,46 @@ "name": "backend", "options": [], "query": { - "query": "label_values(haproxy_server_bytes_in_total{alias=\"$alias\"}, backend)", + "query": "label_values(haproxy_backend_status, proxy)", "refId": "prometheus-backend-Variable-Query" }, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 1, - "tagValuesQuery": null, - "tags": [], - "tagsQuery": null, "type": "query", "useTags": false }, { - "allValue": null, "current": { "isNone": true, "selected": false, "text": "None", "value": "" }, - "datasource": "prometheus", - "definition": "label_values(haproxy_up, alias)", - "description": null, - "error": null, + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, + "definition": "label_values(haproxy_backend_status, instance)", "hide": 0, "includeAll": false, "label": "Load Balancer", "multi": false, - "name": "alias", + "name": "instance", "options": [], "query": { - "query": "label_values(haproxy_up, alias)", + "query": "label_values(haproxy_backend_status, instance)", "refId": "StandardVariableQuery" }, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 1, - "tagValuesQuery": null, - "tags": [], - "tagsQuery": null, "type": "query", "useTags": false }, { - "allValue": null, "current": { "selected": true, "text": [ @@ -1720,10 +1662,11 @@ "$__all" ] }, - "datasource": "prometheus", + "datasource": { + "type": "prometheus", + "uid": "P1809F7CD0C75ACF3" + }, "definition": "", - "description": null, - "error": null, "hide": 0, "includeAll": true, "label": "Server", @@ -1731,23 +1674,20 @@ "name": "server", "options": [], "query": { - "query": "label_values(haproxy_server_bytes_in_total{alias=\"$alias\", backend=~\"$backend\"}, server)", + "query": "label_values(haproxy_server_check_status{proxy=~\"$backend\"}, server)", "refId": "prometheus-server-Variable-Query" }, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 1, - "tagValuesQuery": null, - "tags": [], - "tagsQuery": null, "type": "query", "useTags": false } ] }, "time": { - "from": "now-1h", + "from": "now-60m", "to": "now" }, "timepicker": { @@ -1778,5 +1718,6 @@ "timezone": "browser", "title": "Koios Monitoring", "uid": "7a8vHppnz", - "version": 15 + "version": 1, + "weekStart": "" } diff --git a/topology/topology-guild.json b/topology/topology-guild.json index 452c4012..dd391c83 100644 --- a/topology/topology-guild.json +++ b/topology/topology-guild.json @@ -5,7 +5,7 @@ {"name":"damjan-ssl","addr":"eden-guildnet.koios.rest","port":8453,"ssl":"true"}, {"name":"homer","addr":"95.216.188.94","port":8053}, {"name":"gufmar","addr":"185.161.193.105","port":6029}, - {"name":"HuthS0lo-ssl","addr":"koios-guild.digitalsyndicate.io","port":8453,"ssl":"true"} + {"name":"HuthS0lo-ssl","addr":"koios-guild.themorphium.io","port":8453,"ssl":"true"} ], "Consumers": [] } diff --git a/topology/topology-mainnet.json b/topology/topology-mainnet.json index 62d8be32..ceb8d379 100644 --- a/topology/topology-mainnet.json +++ b/topology/topology-mainnet.json @@ -7,7 +7,7 @@ { "name": "homer-redoracle", "addr": "194.233.71.104", "port": 8053}, { "name": "docker", "addr": "92.204.53.48", "port": 8053}, { "name": "rdlrt2", "addr": "194.36.145.157", "port": 8053}, - { "name": "HuthS0lo1-ssl", "addr": "koios-mainnet.digitalsyndicate.io", "port": 8453, "ssl": "true"}, + { "name": "HuthS0lo1-ssl", "addr": "koios-mainnet.themorphium.io", "port": 8453, "ssl": "true"}, { "name": "reqlez", "addr": "192.234.196.168", "port": 8053} ], "Consumers": [] diff --git a/topology/topology-preprod.json b/topology/topology-preprod.json index d6ae6c09..1c73bc98 100644 --- a/topology/topology-preprod.json +++ b/topology/topology-preprod.json @@ -3,7 +3,8 @@ {"name":"rdlrt","addr":"209.145.50.190","port":18050}, {"name":"eden","addr":"eden-preprod.koios.rest","port":8053}, {"name":"ola-ssl","addr":"koios-preprod.ahlnet.nu","port":2155,"ssl":"true"}, - {"name":"homer","addr":"154.12.248.114","port":8053} + {"name":"homer","addr":"154.12.248.114","port":8053}, + {"name":"huths0lo-ssl","addr":"koios-preprod.themorphiumio","port":8453,"ssl":"true"} ], "Consumers": [] } diff --git a/topology/topology-preview.json b/topology/topology-preview.json index 91344cba..d605fc8a 100644 --- a/topology/topology-preview.json +++ b/topology/topology-preview.json @@ -3,7 +3,8 @@ {"name":"rdlrt","addr":"194.36.145.157","port":28050}, {"name":"damjan","addr":"195.201.129.190","port":8053}, {"name":"ola-ssl","addr":"koios-preview.ahlnet.nu","port":2175,"ssl":"true"}, - {"name":"bbhmm","addr":"74.122.122.121","port":8053} + {"name":"bbhmm","addr":"74.122.122.121","port":8053}, + {"name":"huths0lo-ssl","addr":"koios-preview.themorphiumio","port":8453,"ssl":"true"} ], "Consumers": [] } diff --git a/topology/topology-testnet.json b/topology/topology-testnet.json index 9482954b..55215acf 100644 --- a/topology/topology-testnet.json +++ b/topology/topology-testnet.json @@ -4,7 +4,6 @@ {"name":"damjan","addr":"195.201.129.190","port":8053}, {"name":"homer","addr":"95.216.173.194","port":8053}, {"name":"rdlrt","addr":"89.58.43.194","port":8053}, - {"name":"HuthS0lo1-ssl","addr":"koios-testnet.digitalsyndicate.io","port":8453,"ssl":"true"}, {"name":"reqlez","addr":"192.234.196.167","port":8053} ], "Consumers": [] From f432f1c73d1cf34b28cb78e42429f39bcf6eccf6 Mon Sep 17 00:00:00 2001 From: KoT_B_KocMoce <49576827+hodlonaut@users.noreply.github.com> Date: Fri, 18 Nov 2022 14:43:43 +1100 Subject: [PATCH 26/86] Added homer to preview topology (#134) --- topology/topology-preview.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/topology/topology-preview.json b/topology/topology-preview.json index d605fc8a..ece25c0f 100644 --- a/topology/topology-preview.json +++ b/topology/topology-preview.json @@ -4,7 +4,8 @@ {"name":"damjan","addr":"195.201.129.190","port":8053}, {"name":"ola-ssl","addr":"koios-preview.ahlnet.nu","port":2175,"ssl":"true"}, {"name":"bbhmm","addr":"74.122.122.121","port":8053}, - {"name":"huths0lo-ssl","addr":"koios-preview.themorphiumio","port":8453,"ssl":"true"} + {"name":"huths0lo-ssl","addr":"koios-preview.themorphiumio","port":8453,"ssl":"true"}, + {"name":"homer","addr":"95.216.173.194","port":8053} ], "Consumers": [] } From 4828224a88ffc7679002ea1562599cbadfa94cc3 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Fri, 18 Nov 2022 17:33:56 +1100 Subject: [PATCH 27/86] Bump Koios API version to 1.0.9 (#135) --- specs/results/koiosapi-guild.yaml | 2 +- specs/results/koiosapi-mainnet.yaml | 2 +- specs/results/koiosapi-preprod.yaml | 2 +- specs/results/koiosapi-preview.yaml | 2 +- specs/results/koiosapi-testnet.yaml | 2 +- specs/templates/1-api-info.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index c1bee3ac..23077f8f 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9rc + version: 1.0.9 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 1acf4877..458cbb78 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9rc + version: 1.0.9 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 6ea78cab..2edf9001 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9rc + version: 1.0.9 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 04b840ec..96bb667f 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9rc + version: 1.0.9 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index a3a8a9d8..4b85db5a 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9rc + version: 1.0.9 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 7d3cd41d..78ced506 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -1,6 +1,6 @@ info: title: Koios API - version: 1.0.9rc + version: 1.0.9 description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. From 78c0f2965a74aacaa0c60c5efca286b9fa940553 Mon Sep 17 00:00:00 2001 From: Marko Kungla Date: Fri, 18 Nov 2022 23:10:54 +0200 Subject: [PATCH 28/86] Update README network spec links (#137) Signed-off-by: Marko Kungla --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 22e794bf..57bd3a15 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,9 @@ The specs can be browsed for each network using below: | Network | Link | |:--------|:--------------------------:| | Mainnet | https://api.koios.rest | -| Testnet | https://testnet.koios.rest | | Guild | https://guild.koios.rest | +| PreProd | https://preprod.koios.rest | +| Preview | https://preview.koios.rest | ## Further discussions From e94ef385f539c2d8e11cf2b9bf0fbcac7e584460 Mon Sep 17 00:00:00 2001 From: rdlrt <3169068+rdlrt@users.noreply.github.com> Date: Thu, 24 Nov 2022 07:38:42 +0000 Subject: [PATCH 29/86] Update index.html --- html/index.html | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/html/index.html b/html/index.html index 6aa85703..5aa2898c 100644 --- a/html/index.html +++ b/html/index.html @@ -5,7 +5,7 @@ Koios API Documentation - + + - - - - + +
+ + + + +
From 34fbff64251c53910ec0d8c384d4ea528fef9f16 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Thu, 1 Dec 2022 22:47:40 +1100 Subject: [PATCH 30/86] Remove block_no is NOT NULL filter, as EBBs are valid, and dbsync will handle rollback removing old blocks as well (#140) DBSync now handles rollback removing old blocks once node adopts a chain, thus - no longer requires the filter. If a consumer needs to use sorting on one of the fields that will remain empty for instance, they'd still be able to use `=neq.null` for horizontal filtering --- files/grest/rpc/blocks/block_info.sql | 9 +++------ files/grest/rpc/views/blocks.sql | 2 -- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/files/grest/rpc/blocks/block_info.sql b/files/grest/rpc/blocks/block_info.sql index 45400f38..f1d1f8dd 100644 --- a/files/grest/rpc/blocks/block_info.sql +++ b/files/grest/rpc/blocks/block_info.sql @@ -73,8 +73,7 @@ BEGIN FROM block tB WHERE - block_no = b.block_no - 1 - LIMIT 1 + block_no = b.id - 1 ) AS parent_hash, ( SELECT @@ -82,8 +81,7 @@ BEGIN FROM block tB WHERE - block_no = b.block_no + 1 - LIMIT 1 + block_no = b.id + 1 ) AS child_hash FROM block B @@ -107,8 +105,7 @@ BEGIN tx.block_id = b.id ) block_data ON TRUE WHERE - B.id = ANY (_block_id_list) - AND B.block_no IS NOT NULL; + B.id = ANY (_block_id_list); END; $$; diff --git a/files/grest/rpc/views/blocks.sql b/files/grest/rpc/views/blocks.sql index 6cc5d5c8..b0387e2b 100644 --- a/files/grest/rpc/views/blocks.sql +++ b/files/grest/rpc/views/blocks.sql @@ -19,8 +19,6 @@ CREATE VIEW grest.blocks AS BLOCK B LEFT JOIN SLOT_LEADER SL ON SL.ID = B.SLOT_LEADER_ID LEFT JOIN POOL_HASH PH ON PH.ID = SL.POOL_HASH_ID - WHERE - B.BLOCK_NO IS NOT NULL ORDER BY B.ID DESC; From 6671fc9dba6a9fbd0053e3b2e3c86c7a0e6139f0 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Tue, 6 Dec 2022 19:53:05 +1100 Subject: [PATCH 31/86] Change TTL values in tx_info to string, Fix asset_info/asset_policy_info to show latest mint tx data (#141) ## Description 1. [Minor output (data-type) change] - Convert tx_info: TTL values to string (as some values are far beyond billion) - assists cardano-community/koios-java-client#100 2. [Non-breaking] - Fix asset_info/asset_policy_info to show latest mint tx data (was an error in change before intended to do the same), and update asset_info->tx_hash to show latest (instead of oldest) tx --- files/grest/rpc/assets/asset_info.sql | 6 +++--- files/grest/rpc/assets/asset_policy_info.sql | 2 +- files/grest/rpc/transactions/tx_info.sql | 8 ++++---- specs/results/koiosapi-guild.yaml | 6 +++--- specs/results/koiosapi-mainnet.yaml | 6 +++--- specs/results/koiosapi-preprod.yaml | 6 +++--- specs/results/koiosapi-preview.yaml | 6 +++--- specs/results/koiosapi-testnet.yaml | 6 +++--- specs/templates/4-api-schemas.yaml | 6 +++--- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/files/grest/rpc/assets/asset_info.sql b/files/grest/rpc/assets/asset_info.sql index e5109cb1..076244b2 100644 --- a/files/grest/rpc/assets/asset_info.sql +++ b/files/grest/rpc/assets/asset_info.sql @@ -47,7 +47,7 @@ BEGIN WHERE MTM.ident = _asset_id AND MTM.quantity > 0 ORDER BY - MTM.tx_id ASC + MTM.tx_id DESC LIMIT 1 ) AS tx_hash, minting_data.total_supply, @@ -62,15 +62,15 @@ BEGIN ) as minting_tx_metadata FROM ( - SELECT TM.key, TM.json, MAX(MTM.tx_id) + SELECT TM.key, TM.json, MTM.tx_id FROM tx_metadata TM INNER JOIN ma_tx_mint MTM ON TM.tx_id = MTM.tx_id WHERE MTM.ident = _asset_id + AND MTM.tx_id = (SELECT max(tx_id) from ma_tx_mint where ident = _asset_id) AND MTM.quantity > 0 AND TM.JSON IS NOT NULL - GROUP BY TM.key, TM.json ) x ) AS minting_tx_metadata, ( diff --git a/files/grest/rpc/assets/asset_policy_info.sql b/files/grest/rpc/assets/asset_policy_info.sql index ccfdc54f..77032e27 100644 --- a/files/grest/rpc/assets/asset_policy_info.sql +++ b/files/grest/rpc/assets/asset_policy_info.sql @@ -33,7 +33,7 @@ BEGIN MTM.ident = ANY(_policy_asset_ids) AND MTM.quantity > 0 ORDER BY MTM.ident, - TM.tx_id ASC + TM.tx_id DESC ), token_registry_metadatas AS ( SELECT DISTINCT ON (asset_policy, asset_name) diff --git a/files/grest/rpc/transactions/tx_info.sql b/files/grest/rpc/transactions/tx_info.sql index ed106264..f40cbbef 100644 --- a/files/grest/rpc/transactions/tx_info.sql +++ b/files/grest/rpc/transactions/tx_info.sql @@ -12,8 +12,8 @@ CREATE FUNCTION grest.tx_info (_tx_hashes text[]) total_output text, fee text, deposit text, - invalid_before word64type, - invalid_after word64type, + invalid_before text, + invalid_after text, collateral_inputs jsonb, collateral_output jsonb, reference_inputs jsonb, @@ -696,8 +696,8 @@ BEGIN ATX.total_output::text, ATX.fee::text, ATX.deposit::text, - ATX.invalid_before, - ATX.invalid_after, + ATX.invalid_before::text, + ATX.invalid_after::text, COALESCE(( SELECT JSONB_AGG(tx_collateral_inputs) FROM ( diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 23077f8f..9730a60c 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -2723,11 +2723,11 @@ components: description: Total Deposits included in transaction (for example, if it is registering a pool/key) example: 0 invalid_before: - type: integer + type: string description: Slot before which transaction cannot be validated (if supplied, else null) nullable: true invalid_after: - type: integer + type: string description: Slot after which transaction cannot be validated example: 42332172 nullable: true @@ -3107,7 +3107,7 @@ components: example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 minting_tx_hash: type: string - description: Hash of the first mint transaction + description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 mint_cnt: type: integer diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 458cbb78..4d1b2051 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -2723,11 +2723,11 @@ components: description: Total Deposits included in transaction (for example, if it is registering a pool/key) example: 0 invalid_before: - type: integer + type: string description: Slot before which transaction cannot be validated (if supplied, else null) nullable: true invalid_after: - type: integer + type: string description: Slot after which transaction cannot be validated example: 42332172 nullable: true @@ -3107,7 +3107,7 @@ components: example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 minting_tx_hash: type: string - description: Hash of the first mint transaction + description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 mint_cnt: type: integer diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 2edf9001..05522d4b 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -2723,11 +2723,11 @@ components: description: Total Deposits included in transaction (for example, if it is registering a pool/key) example: 0 invalid_before: - type: integer + type: string description: Slot before which transaction cannot be validated (if supplied, else null) nullable: true invalid_after: - type: integer + type: string description: Slot after which transaction cannot be validated example: 42332172 nullable: true @@ -3107,7 +3107,7 @@ components: example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 minting_tx_hash: type: string - description: Hash of the first mint transaction + description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 mint_cnt: type: integer diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 96bb667f..3dacabd3 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -2723,11 +2723,11 @@ components: description: Total Deposits included in transaction (for example, if it is registering a pool/key) example: 0 invalid_before: - type: integer + type: string description: Slot before which transaction cannot be validated (if supplied, else null) nullable: true invalid_after: - type: integer + type: string description: Slot after which transaction cannot be validated example: 42332172 nullable: true @@ -3107,7 +3107,7 @@ components: example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 minting_tx_hash: type: string - description: Hash of the first mint transaction + description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 mint_cnt: type: integer diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 4b85db5a..bd83626c 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -2723,11 +2723,11 @@ components: description: Total Deposits included in transaction (for example, if it is registering a pool/key) example: 0 invalid_before: - type: integer + type: string description: Slot before which transaction cannot be validated (if supplied, else null) nullable: true invalid_after: - type: integer + type: string description: Slot after which transaction cannot be validated example: 42332172 nullable: true @@ -3107,7 +3107,7 @@ components: example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 minting_tx_hash: type: string - description: Hash of the first mint transaction + description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 mint_cnt: type: integer diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index d7e070e7..7f3f549d 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -1085,11 +1085,11 @@ schemas: description: Total Deposits included in transaction (for example, if it is registering a pool/key) example: 0 invalid_before: - type: integer + type: string description: Slot before which transaction cannot be validated (if supplied, else null) nullable: true invalid_after: - type: integer + type: string description: Slot after which transaction cannot be validated example: 42332172 nullable: true @@ -1469,7 +1469,7 @@ schemas: example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 minting_tx_hash: type: string - description: Hash of the first mint transaction + description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 mint_cnt: type: integer From d206672a4d6de5f039aae884455d9271d78de571 Mon Sep 17 00:00:00 2001 From: KoT_B_KocMoce <49576827+hodlonaut@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:53:02 +1100 Subject: [PATCH 32/86] Null fields in latest epoch info (#143) * Koios artifacts issue #139 - null fields in latest epoch response - added and used new flag _include_unstarted_epoch to control whether to include in results the not-yet-commenced epoch after the nonce is created for it in the koios cache table near the end of the current epoch (useful for getting active stake snapshot information) * Added information about new parameter type for _include_unstarted_epoch and updated parameter list for epoch_info * Renamed unstarted to next in parameter name, ran the create spec as instructed by Pri * Removed unncessary drop statement, as we are using -q to drop all functions as a part of release process (thanks Pri) Co-authored-by: Greg Beresnev --- files/grest/rpc/epoch/epoch_info.sql | 9 +++++---- specs/results/koiosapi-guild.yaml | 11 +++++++++++ specs/results/koiosapi-mainnet.yaml | 11 +++++++++++ specs/results/koiosapi-preprod.yaml | 11 +++++++++++ specs/results/koiosapi-preview.yaml | 11 +++++++++++ specs/results/koiosapi-testnet.yaml | 11 +++++++++++ specs/templates/2-api-params.yaml | 10 ++++++++++ specs/templates/api-main.yaml | 1 + 8 files changed, 71 insertions(+), 4 deletions(-) diff --git a/files/grest/rpc/epoch/epoch_info.sql b/files/grest/rpc/epoch/epoch_info.sql index 757c7fa5..8242aaff 100644 --- a/files/grest/rpc/epoch/epoch_info.sql +++ b/files/grest/rpc/epoch/epoch_info.sql @@ -1,4 +1,4 @@ -CREATE FUNCTION grest.epoch_info (_epoch_no numeric DEFAULT NULL) +CREATE OR REPLACE FUNCTION grest.epoch_info (_epoch_no numeric DEFAULT NULL, _include_next_epoch boolean DEFAULT false) RETURNS TABLE ( epoch_no word31type, out_sum text, @@ -46,9 +46,10 @@ BEGIN grest.epoch_info_cache ei LEFT JOIN grest.EPOCH_ACTIVE_STAKE_CACHE eas ON eas.epoch_no = ei.epoch_no WHERE - ei.epoch_no::text LIKE CASE WHEN _epoch_no IS NULL THEN '%' ELSE _epoch_no::text END; + ei.epoch_no::text LIKE CASE WHEN _epoch_no IS NULL THEN '%' ELSE _epoch_no::text END + AND + (_include_next_epoch OR ei.i_first_block_time::integer is not null); END; $$; -COMMENT ON FUNCTION grest.epoch_info IS 'Get the epoch information, all epochs if no epoch specified'; - +COMMENT ON FUNCTION grest.epoch_info IS 'Get the epoch information, all epochs if no epoch specified. If _include_next_epoch is set to true, also return active stake calculation for next epoch if available'; diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 9730a60c..e98fcc79 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -288,6 +288,7 @@ paths: - Epoch parameters: - $ref: "#/components/parameters/_epoch_no" + - $ref: "#/components/parameters/_include_next_epoch" responses: "200": description: Array of detailed summary for each epoch @@ -1404,6 +1405,16 @@ components: in: query required: false allowEmptyValue: false + _include_next_epoch: + deprecated: false + name: _history + description: Include information about nearing but not yet started epoch, to get access to active stake snapshot information if available + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _pool_bech32: deprecated: false name: _pool_bech32 diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 4d1b2051..09b9eb37 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -288,6 +288,7 @@ paths: - Epoch parameters: - $ref: "#/components/parameters/_epoch_no" + - $ref: "#/components/parameters/_include_next_epoch" responses: "200": description: Array of detailed summary for each epoch @@ -1404,6 +1405,16 @@ components: in: query required: false allowEmptyValue: false + _include_next_epoch: + deprecated: false + name: _history + description: Include information about nearing but not yet started epoch, to get access to active stake snapshot information if available + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _pool_bech32: deprecated: false name: _pool_bech32 diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 05522d4b..78c6d942 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -288,6 +288,7 @@ paths: - Epoch parameters: - $ref: "#/components/parameters/_epoch_no" + - $ref: "#/components/parameters/_include_next_epoch" responses: "200": description: Array of detailed summary for each epoch @@ -1404,6 +1405,16 @@ components: in: query required: false allowEmptyValue: false + _include_next_epoch: + deprecated: false + name: _history + description: Include information about nearing but not yet started epoch, to get access to active stake snapshot information if available + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _pool_bech32: deprecated: false name: _pool_bech32 diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 3dacabd3..59a06a50 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -288,6 +288,7 @@ paths: - Epoch parameters: - $ref: "#/components/parameters/_epoch_no" + - $ref: "#/components/parameters/_include_next_epoch" responses: "200": description: Array of detailed summary for each epoch @@ -1404,6 +1405,16 @@ components: in: query required: false allowEmptyValue: false + _include_next_epoch: + deprecated: false + name: _history + description: Include information about nearing but not yet started epoch, to get access to active stake snapshot information if available + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _pool_bech32: deprecated: false name: _pool_bech32 diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index bd83626c..7de592b3 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -288,6 +288,7 @@ paths: - Epoch parameters: - $ref: "#/components/parameters/_epoch_no" + - $ref: "#/components/parameters/_include_next_epoch" responses: "200": description: Array of detailed summary for each epoch @@ -1404,6 +1405,16 @@ components: in: query required: false allowEmptyValue: false + _include_next_epoch: + deprecated: false + name: _history + description: Include information about nearing but not yet started epoch, to get access to active stake snapshot information if available + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _pool_bech32: deprecated: false name: _pool_bech32 diff --git a/specs/templates/2-api-params.yaml b/specs/templates/2-api-params.yaml index 2d75d18d..3cf58b97 100644 --- a/specs/templates/2-api-params.yaml +++ b/specs/templates/2-api-params.yaml @@ -160,6 +160,16 @@ parameters: in: query required: false allowEmptyValue: false + _include_next_epoch: + deprecated: false + name: _history + description: Include information about nearing but not yet started epoch, to get access to active stake snapshot information if available + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _pool_bech32: deprecated: false name: _pool_bech32 diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index 95b83e8a..e5069d07 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -72,6 +72,7 @@ paths: - Epoch parameters: - $ref: "#/components/parameters/_epoch_no" + - $ref: "#/components/parameters/_include_next_epoch" responses: "200": description: Array of detailed summary for each epoch From 583b1f6ddfb38576a5472bdc20847ec930101e5a Mon Sep 17 00:00:00 2001 From: "Ola [AHLNET]" Date: Tue, 20 Dec 2022 13:57:57 +0100 Subject: [PATCH 33/86] New asset_info_cache table (#142) * Adds a new asset_info_cache table - New asset_info_cache table - Update rpc's where it makes sense to add decimals - New asset_info_bulk rpc that takes bulk input. Current asset_info untouched and to be phased out. Co-authored-by: rdlrt <3169068+rdlrt@users.noreply.github.com> --- .../cron/jobs/asset-info-cache-update.sh | 6 + .../rpc/01_cached_tables/asset_info_cache.sql | 177 ++++++++++++++++++ files/grest/rpc/account/account_assets.sql | 3 + files/grest/rpc/address/address_assets.sql | 3 + files/grest/rpc/address/address_info.sql | 2 + files/grest/rpc/assets/asset_info.sql | 95 +--------- files/grest/rpc/assets/asset_info_bulk.sql | 79 ++++++++ files/grest/rpc/assets/asset_policy_info.sql | 122 ++++-------- files/grest/rpc/assets/asset_policy_list.sql | 32 ++++ files/grest/rpc/transactions/tx_info.sql | 12 ++ files/grest/rpc/transactions/tx_utxos.sql | 4 + specs/results/koiosapi-guild.yaml | 61 ++++++ specs/results/koiosapi-mainnet.yaml | 61 ++++++ specs/results/koiosapi-preprod.yaml | 61 ++++++ specs/results/koiosapi-preview.yaml | 61 ++++++ specs/results/koiosapi-testnet.yaml | 61 ++++++ specs/templates/3-api-requestBodies.yaml | 22 ++- specs/templates/4-api-schemas.yaml | 21 +++ specs/templates/api-main.yaml | 20 ++ specs/templates/example-map.json | 14 ++ 20 files changed, 736 insertions(+), 181 deletions(-) create mode 100644 files/grest/cron/jobs/asset-info-cache-update.sh create mode 100644 files/grest/rpc/01_cached_tables/asset_info_cache.sql create mode 100644 files/grest/rpc/assets/asset_info_bulk.sql create mode 100644 files/grest/rpc/assets/asset_policy_list.sql diff --git a/files/grest/cron/jobs/asset-info-cache-update.sh b/files/grest/cron/jobs/asset-info-cache-update.sh new file mode 100644 index 00000000..c3ad5bca --- /dev/null +++ b/files/grest/cron/jobs/asset-info-cache-update.sh @@ -0,0 +1,6 @@ +#!/bin/bash +DB_NAME=cexplorer + +echo "$(date +%F_%H:%M:%S) Running asset info cache update..." +psql ${DB_NAME} -qbt -c "SELECT grest.asset_info_cache_update();" 2>&1 1>/dev/null +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/rpc/01_cached_tables/asset_info_cache.sql b/files/grest/rpc/01_cached_tables/asset_info_cache.sql new file mode 100644 index 00000000..7cba903d --- /dev/null +++ b/files/grest/rpc/01_cached_tables/asset_info_cache.sql @@ -0,0 +1,177 @@ +CREATE TABLE IF NOT EXISTS grest.asset_info_cache ( + asset_id bigint PRIMARY KEY NOT NULL, + creation_time date, + total_supply numeric, + decimals integer, + mint_cnt bigint, + burn_cnt bigint, + first_mint_tx_id bigint, + first_mint_keys text[], + last_mint_tx_id bigint, + last_mint_keys text[] +); + +CREATE INDEX IF NOT EXISTS idx_first_mint_tx_id ON grest.asset_info_cache (first_mint_tx_id); +CREATE INDEX IF NOT EXISTS idx_last_mint_tx_id ON grest.asset_info_cache (last_mint_tx_id); + +CREATE OR REPLACE FUNCTION grest.asset_info_cache_update () + RETURNS void + LANGUAGE plpgsql + AS $$ +DECLARE + _lastest_tx_id bigint; + _asset_info_cache_last_tx_id bigint; + _asset_id_list bigint[]; +BEGIN + -- Check previous cache update completed before running + IF ( + SELECT + COUNT(pid) > 1 + FROM + pg_stat_activity + WHERE + state = 'active' AND query ILIKE '%grest.asset_info_cache_update%' + AND datname = (SELECT current_database()) + ) THEN + RAISE EXCEPTION 'Previous asset_info_cache_update query still running but should have completed! Exiting...'; + END IF; + + SELECT + MAX(id) INTO _lastest_tx_id + FROM + public.tx; + + SELECT + COALESCE(last_value::bigint,100) - 100 INTO _asset_info_cache_last_tx_id + FROM + grest.control_table + WHERE + key = 'asset_info_cache_last_tx_id'; + + IF _asset_info_cache_last_tx_id IS NULL THEN + RAISE NOTICE 'Asset info cache table is empty, deleting all previous cache records and starting initial population...'; + TRUNCATE TABLE grest.asset_info_cache; + ELSE + RAISE NOTICE 'Updating asset info based on data from transaction id in range % - % ...', _asset_info_cache_last_tx_id, _lastest_tx_id; + SELECT + ARRAY_AGG(DISTINCT ident) INTO _asset_id_list + FROM + ma_tx_mint + WHERE + tx_id > _asset_info_cache_last_tx_id; + END IF; + + WITH + + tx_mint_meta AS ( + SELECT + mtm.ident, + MIN(mtm.tx_id) AS first_mint_tx_id, + MAX(mtm.tx_id) AS last_mint_tx_id + FROM + ma_tx_mint mtm + INNER JOIN tx_metadata tm ON tm.tx_id = mtm.tx_id + WHERE + CASE WHEN _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL + THEN + mtm.ident = any(_asset_id_list) + AND mtm.tx_id > _asset_info_cache_last_tx_id + ELSE TRUE + END + AND mtm.quantity > 0 + AND tm.json IS NOT NULL + GROUP BY mtm.ident + ), + + tx_mint_nometa AS ( + SELECT + mtm.ident, + MIN(mtm.tx_id) AS first_mint_tx_id, + MAX(mtm.tx_id) AS last_mint_tx_id + FROM + ma_tx_mint mtm + LEFT JOIN tx_mint_meta ON tx_mint_meta.ident = mtm.ident + WHERE + CASE WHEN _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL + THEN + mtm.id = any(_asset_id_list) + AND mtm.tx_id > _asset_info_cache_last_tx_id + ELSE TRUE + END + AND tx_mint_meta IS NULL + AND mtm.quantity > 0 + GROUP BY mtm.ident + ), + + tx_meta AS ( + SELECT + tmm.ident, + tmm.first_mint_tx_id, + ARRAY_AGG(tm.key) FILTER(WHERE tm.tx_id = tmm.first_mint_tx_id) AS first_mint_keys, + tmm.last_mint_tx_id, + ARRAY_AGG(tm.key) FILTER(WHERE tm.tx_id = tmm.last_mint_tx_id) AS last_mint_keys + FROM + tx_mint_meta tmm + INNER JOIN tx_metadata tm ON tm.tx_id = tmm.first_mint_tx_id OR tm.tx_id = tmm.last_mint_tx_id + GROUP BY tmm.ident, tmm.first_mint_tx_id, tmm.last_mint_tx_id + -- + UNION ALL + -- + SELECT + tx_mint_nometa.ident, + tx_mint_nometa.first_mint_tx_id, + '{}', + tx_mint_nometa.last_mint_tx_id, + '{}' + FROM + tx_mint_nometa + ) + + INSERT INTO grest.asset_info_cache + SELECT + ma.id, + MIN(B.time) AS creation_time, + SUM(mtm.quantity) AS total_supply, + COALESCE(arc.decimals, 0) AS decimals, + SUM(CASE WHEN mtm.quantity > 0 THEN 1 ELSE 0 END) AS mint_cnt, + SUM(CASE WHEN mtm.quantity < 0 THEN 1 ELSE 0 END) AS burn_cnt, + tm.first_mint_tx_id, + tm.first_mint_keys, + tm.last_mint_tx_id, + tm.last_mint_keys + FROM + multi_asset ma + INNER JOIN ma_tx_mint mtm ON mtm.ident = ma.id + INNER JOIN tx ON tx.id = mtm.tx_id + INNER JOIN block b ON b.id = tx.block_id + INNER JOIN tx_meta tm ON tm.ident = ma.id + LEFT JOIN grest.asset_registry_cache arc ON DECODE(arc.asset_policy, 'hex') = ma.policy AND DECODE(arc.asset_name, 'hex') = ma.name + WHERE + CASE WHEN _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL + THEN + mtm.id = any(_asset_id_list) + ELSE TRUE + END + GROUP BY ma.id, arc.decimals, tm.first_mint_tx_id, tm.first_mint_keys, tm.last_mint_tx_id, tm.last_mint_keys + ON CONFLICT (asset_id) + DO UPDATE SET + creation_time = excluded.creation_time, + total_supply = excluded.total_supply, + decimals = excluded.decimals, + mint_cnt = excluded.mint_cnt, + burn_cnt = excluded.burn_cnt, + last_mint_tx_id = excluded.last_mint_tx_id, + last_mint_keys = excluded.last_mint_keys; + + IF _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL THEN + RAISE NOTICE '% assets added or updated', ARRAY_LENGTH(_asset_id_list, 1); + END IF; + + -- GREST control table entry + PERFORM grest.update_control_table( + 'asset_info_cache_last_tx_id', + _lastest_tx_id::text + ); + +END; +$$; diff --git a/files/grest/rpc/account/account_assets.sql b/files/grest/rpc/account/account_assets.sql index b07acb56..2e2c7b93 100644 --- a/files/grest/rpc/account/account_assets.sql +++ b/files/grest/rpc/account/account_assets.sql @@ -22,12 +22,14 @@ BEGIN ma.policy, ma.name, ma.fingerprint, + aic.decimals, SUM(mtx.quantity) as quantity FROM MA_TX_OUT MTX INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID INNER JOIN STAKE_ADDRESS sa ON sa.id = TXO.stake_address_id + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id LEFT JOIN TX_IN on TXO.TX_ID = TX_IN.TX_OUT_ID AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint WHERE @@ -48,6 +50,7 @@ BEGIN 'policy_id', ENCODE(aa.policy, 'hex'), 'asset_name', ENCODE(aa.name, 'hex'), 'fingerprint', aa.fingerprint, + 'decimals', COALESCE(aa.decimals, 0), 'quantity', aa.quantity::text ) ) as assets diff --git a/files/grest/rpc/address/address_assets.sql b/files/grest/rpc/address/address_assets.sql index 59a4efb7..f46c6548 100644 --- a/files/grest/rpc/address/address_assets.sql +++ b/files/grest/rpc/address/address_assets.sql @@ -14,10 +14,12 @@ BEGIN ma.policy, ma.name, ma.fingerprint, + aic.decimals, SUM(mtx.quantity) as quantity FROM MA_TX_OUT MTX INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID LEFT JOIN TX_IN ON TXO.TX_ID = TX_IN.TX_OUT_ID AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint @@ -39,6 +41,7 @@ BEGIN 'policy_id', ENCODE(aa.policy, 'hex'), 'asset_name', ENCODE(aa.name, 'hex'), 'fingerprint', aa.fingerprint, + 'decimals', aa.decimals, 'quantity', aa.quantity::text ) ) as asset_list diff --git a/files/grest/rpc/address/address_info.sql b/files/grest/rpc/address/address_info.sql index ef6a6cdb..3f0e43c4 100644 --- a/files/grest/rpc/address/address_info.sql +++ b/files/grest/rpc/address/address_info.sql @@ -90,11 +90,13 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTX.quantity::text )) FROM ma_tx_out MTX INNER JOIN multi_asset MA ON MA.id = MTX.ident + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id WHERE MTX.tx_out_id = au.txo_id ), diff --git a/files/grest/rpc/assets/asset_info.sql b/files/grest/rpc/assets/asset_info.sql index 076244b2..b3636ef2 100644 --- a/files/grest/rpc/assets/asset_info.sql +++ b/files/grest/rpc/assets/asset_info.sql @@ -14,100 +14,11 @@ CREATE OR REPLACE FUNCTION grest.asset_info (_asset_policy text, _asset_name tex ) LANGUAGE PLPGSQL AS $$ -DECLARE - _asset_policy_decoded bytea; - _asset_name_decoded bytea; - _asset_id int; BEGIN - SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - - SELECT DECODE( - CASE WHEN _asset_name IS NULL - THEN '' - ELSE - _asset_name - END, - 'hex' - ) INTO _asset_name_decoded; - - SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; - + RETURN QUERY - SELECT - _asset_policy, - _asset_name, - ENCODE(_asset_name_decoded, 'escape'), - MA.fingerprint, - ( - SELECT - ENCODE(tx.hash, 'hex') - FROM - ma_tx_mint MTM - INNER JOIN tx ON tx.id = MTM.tx_id - WHERE - MTM.ident = _asset_id AND MTM.quantity > 0 - ORDER BY - MTM.tx_id DESC - LIMIT 1 - ) AS tx_hash, - minting_data.total_supply, - minting_data.mint_cnt, - minting_data.burn_cnt, - EXTRACT(epoch from minting_data.date)::integer, - ( - SELECT - JSONB_OBJECT_AGG( - key::text, - json - ) as minting_tx_metadata - FROM - ( - SELECT TM.key, TM.json, MTM.tx_id - FROM tx_metadata TM - INNER JOIN ma_tx_mint MTM - ON TM.tx_id = MTM.tx_id - WHERE - MTM.ident = _asset_id - AND MTM.tx_id = (SELECT max(tx_id) from ma_tx_mint where ident = _asset_id) - AND MTM.quantity > 0 - AND TM.JSON IS NOT NULL - ) x - ) AS minting_tx_metadata, - ( - SELECT - JSON_BUILD_OBJECT( - 'name', ARC.name, - 'description', ARC.description, - 'ticker', ARC.ticker, - 'url', ARC.url, - 'logo', ARC.logo, - 'decimals', ARC.decimals - ) as metadata - FROM - grest.asset_registry_cache ARC - WHERE - ARC.asset_policy = _asset_policy - AND - ARC.asset_name = _asset_name - LIMIT 1 - ) AS token_registry_metadata - FROM - multi_asset MA - LEFT JOIN LATERAL ( - SELECT - MIN(B.time) AS date, - SUM(MTM.quantity)::text AS total_supply, - SUM(CASE WHEN quantity > 0 then 1 else 0 end) AS mint_cnt, - SUM(CASE WHEN quantity < 0 then 1 else 0 end) AS burn_cnt - FROM - ma_tx_mint MTM - INNER JOIN tx ON tx.id = MTM.tx_id - INNER JOIN block B ON B.id = tx.block_id - WHERE - MTM.ident = MA.id - ) minting_data ON TRUE - WHERE - MA.id = _asset_id; + SELECT grest.asset_info_bulk(array[array[_asset_policy, _asset_name]]); + END; $$; diff --git a/files/grest/rpc/assets/asset_info_bulk.sql b/files/grest/rpc/assets/asset_info_bulk.sql new file mode 100644 index 00000000..f34d68d0 --- /dev/null +++ b/files/grest/rpc/assets/asset_info_bulk.sql @@ -0,0 +1,79 @@ +CREATE OR REPLACE FUNCTION grest.asset_info (_asset_list text[][]) + RETURNS TABLE ( + policy_id text, + asset_name text, + asset_name_ascii text, + fingerprint character varying, + minting_tx_hash text, + total_supply text, + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, + minting_tx_metadata jsonb, + token_registry_metadata json + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_id_list bigint[]; +BEGIN + + -- find all asset id's based on nested array input + SELECT INTO _asset_id_list ARRAY_AGG(id) + FROM ( + SELECT DISTINCT mu.id + FROM ( + SELECT + DECODE(asset_list->>0, 'hex') AS policy, + DECODE(asset_list->>1, 'hex') AS name + FROM + JSONB_ARRAY_ELEMENTS(TO_JSONB(_asset_list)) asset_list + ) ald + INNER JOIN multi_asset mu ON mu.policy = ald.policy AND mu.name = ald.name + ) AS tmp; + + RETURN QUERY + + SELECT + ENCODE(ma.policy, 'hex'), + ENCODE(ma.name, 'hex'), + ENCODE(ma.name, 'escape'), + ma.fingerprint, + ENCODE(tx.hash, 'hex'), + aic.total_supply::text, + aic.mint_cnt, + aic.burn_cnt, + EXTRACT(epoch from aic.creation_time)::integer, + metadata.minting_tx_metadata, + CASE WHEN arc.name IS NULL THEN NULL + ELSE + JSON_BUILD_OBJECT( + 'name', arc.name, + 'description', arc.description, + 'ticker', arc.ticker, + 'url', arc.url, + 'logo', arc.logo, + 'decimals', arc.decimals + ) + END + FROM + multi_asset ma + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + LEFT JOIN tx ON tx.id = aic.last_mint_tx_id + LEFT JOIN grest.asset_registry_cache arc ON DECODE(arc.asset_policy, 'hex') = ma.policy AND DECODE(arc.asset_name, 'hex') = ma.name + LEFT JOIN LATERAL ( + SELECT + JSONB_OBJECT_AGG( + key::text, + json + ) AS minting_tx_metadata + FROM + tx_metadata tm + WHERE + tm.tx_id = tx.id + ) metadata ON TRUE + WHERE + ma.id = any (_asset_id_list); + +END; +$$; diff --git a/files/grest/rpc/assets/asset_policy_info.sql b/files/grest/rpc/assets/asset_policy_info.sql index 77032e27..fd3c8d79 100644 --- a/files/grest/rpc/assets/asset_policy_info.sql +++ b/files/grest/rpc/assets/asset_policy_info.sql @@ -15,98 +15,44 @@ DECLARE _policy_asset_ids bigint[]; BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - SELECT INTO _policy_asset_ids ARRAY_AGG(id) FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded; - - RETURN QUERY ( - WITH - minting_tx_metadatas AS ( - SELECT DISTINCT ON (MTM.ident) - MTM.ident, - JSONB_BUILD_OBJECT( - 'key', TM.key::text, - 'json', TM.json - ) AS metadata - FROM - tx_metadata TM - INNER JOIN ma_tx_mint MTM on MTM.tx_id = TM.tx_id - WHERE - MTM.ident = ANY(_policy_asset_ids) AND MTM.quantity > 0 - ORDER BY - MTM.ident, - TM.tx_id DESC - ), - token_registry_metadatas AS ( - SELECT DISTINCT ON (asset_policy, asset_name) - asset_policy, - ARC.asset_name, - JSONB_BUILD_OBJECT( - 'name', ARC.name, - 'description', ARC.description, - 'ticker', ARC.ticker, - 'url', ARC.url, - 'logo', ARC.logo, - 'decimals', ARC.decimals - ) as metadata - FROM - grest.asset_registry_cache ARC - WHERE - asset_policy = _asset_policy - ORDER BY - asset_policy, - ARC.asset_name - ), - total_supplies AS ( - SELECT - MTM.ident, - SUM(MTM.quantity)::text AS amount - FROM - ma_tx_mint MTM - WHERE - MTM.ident = ANY(_policy_asset_ids) - GROUP BY - MTM.ident - ), - creation_times AS ( + + RETURN QUERY + SELECT + ENCODE(ma.name, 'hex') AS asset_name, + ENCODE(ma.name, 'escape') AS asset_name_ascii, + ma.fingerprint, + metadata.minting_tx_metadata, + CASE WHEN arc.name IS NULL THEN NULL + ELSE + JSON_BUILD_OBJECT( + 'name', arc.name, + 'description', arc.description, + 'ticker', arc.ticker, + 'url', arc.url, + 'logo', arc.logo, + 'decimals', arc.decimals + ) + END, + aic.total_supply, + EXTRACT(epoch from aic.creation_time)::integer + FROM + multi_asset ma + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + LEFT JOIN tx ON tx.id = aic.last_mint_tx_id + LEFT JOIN grest.asset_registry_cache arc ON DECODE(arc.asset_policy, 'hex') = ma.policy AND DECODE(arc.asset_name, 'hex') = ma.name + LEFT JOIN LATERAL ( SELECT - MTM.ident, - MIN(B.time) AS date + JSONB_OBJECT_AGG( + key::text, + json + ) AS minting_tx_metadata FROM - ma_tx_mint MTM - INNER JOIN tx ON tx.id = MTM.tx_id - INNER JOIN block B ON B.id = tx.block_id + tx_metadata tm WHERE - MTM.ident = ANY(_policy_asset_ids) - GROUP BY - MTM.ident - ) - - SELECT - ENCODE(MA.name, 'hex') as asset_name, - ENCODE(MA.name, 'escape') as asset_name_ascii, - MA.fingerprint as fingerprint, - mtm.metadata as minting_tx_metadata, - trm.metadata as token_registry_metadata, - ts.amount::text as total_supply, - EXTRACT(epoch from ct.date)::integer - FROM - multi_asset MA - LEFT JOIN minting_tx_metadatas mtm ON mtm.ident = MA.id - LEFT JOIN token_registry_metadatas trm ON trm.asset_policy = _asset_policy - AND DECODE(trm.asset_name, 'hex') = MA.name - INNER JOIN total_supplies ts on ts.ident = MA.id - INNER JOIN creation_times ct ON ct.ident = MA.id + tm.tx_id = tx.id + ) metadata ON TRUE WHERE - MA.id = ANY(_policy_asset_ids) - GROUP BY - MA.policy, - MA.name, - MA.fingerprint, - MA.id, - mtm.metadata, - trm.metadata, - ts.amount, - ct.date - ); + ma.policy = _asset_policy_decoded; END; $$; diff --git a/files/grest/rpc/assets/asset_policy_list.sql b/files/grest/rpc/assets/asset_policy_list.sql new file mode 100644 index 00000000..b03ad416 --- /dev/null +++ b/files/grest/rpc/assets/asset_policy_list.sql @@ -0,0 +1,32 @@ +CREATE FUNCTION grest.asset_policy_info (_asset_policy text) + RETURNS TABLE ( + asset_name text, + fingerprint varchar, + total_supply text, + decimals integer + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; +BEGIN + + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + + RETURN QUERY + + SELECT + ENCODE(ma.name, 'hex') AS asset_name, + ma.fingerprint AS fingerprint, + aic.total_supply, + aic.decimals + FROM + multi_asset ma + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + WHERE + ma.policy = _asset_policy_decoded; + +END; +$$; + +COMMENT ON FUNCTION grest.asset_policy_info IS 'Get a list of all asset under a policy'; diff --git a/files/grest/rpc/transactions/tx_info.sql b/files/grest/rpc/transactions/tx_info.sql index f40cbbef..4a42e0b1 100644 --- a/files/grest/rpc/transactions/tx_info.sql +++ b/files/grest/rpc/transactions/tx_info.sql @@ -95,6 +95,7 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTO.quantity::text ) END @@ -126,6 +127,7 @@ BEGIN LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id WHERE @@ -148,6 +150,7 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTO.quantity::text ) END @@ -179,6 +182,7 @@ BEGIN LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id WHERE @@ -201,6 +205,7 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTO.quantity::text ) END @@ -232,6 +237,7 @@ BEGIN LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id WHERE @@ -254,6 +260,7 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTO.quantity::text ) END @@ -283,6 +290,7 @@ BEGIN LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id WHERE @@ -305,6 +313,7 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTO.quantity::text ) END @@ -334,6 +343,7 @@ BEGIN LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id WHERE @@ -372,11 +382,13 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', COALESCE(aic.decimals, 0), 'quantity', MTM.quantity::text ) AS data FROM ma_tx_mint MTM INNER JOIN MULTI_ASSET MA ON MA.id = MTM.ident + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id WHERE MTM.tx_id = ANY (_tx_id_list) ) AS tmp diff --git a/files/grest/rpc/transactions/tx_utxos.sql b/files/grest/rpc/transactions/tx_utxos.sql index 6c3514ef..b77a6406 100644 --- a/files/grest/rpc/transactions/tx_utxos.sql +++ b/files/grest/rpc/transactions/tx_utxos.sql @@ -57,6 +57,7 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTO.quantity::text ) END @@ -69,6 +70,7 @@ BEGIN LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id WHERE tx_in.tx_in_id = ANY (_tx_id_list) ), @@ -88,6 +90,7 @@ BEGIN 'policy_id', ENCODE(MA.policy, 'hex'), 'asset_name', ENCODE(MA.name, 'hex'), 'fingerprint', MA.fingerprint, + 'decimals', aic.decimals, 'quantity', MTO.quantity::text ) END @@ -98,6 +101,7 @@ BEGIN LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id LEFT JOIN multi_asset MA ON MA.id = MTO.ident + LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id WHERE tx_out.tx_id = ANY (_tx_id_list) ) diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index e98fcc79..94306626 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -859,6 +859,26 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Information description: Get the information of an asset including first minting & token registry metadata + post: + tags: + - Asset + requestBody: + $ref: "#/components/requestBodies/asset_list" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata /asset_history: #RPC get: tags: @@ -1646,6 +1666,26 @@ components: _datum_hashes: - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e'] + - ['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e'] securitySchemes: {} schemas: tip: @@ -2661,6 +2701,10 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + type: integer + description: Asset decimals + example: 6 quantity: type: string description: Asset quantity owned by account @@ -2844,6 +2888,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of assets on the UTxO @@ -2875,6 +2921,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3216,6 +3264,19 @@ components: creation_time: type: string $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_policy_list: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_supply: + $ref: "#/components/schemas/asset_info/items/properties/total_supply" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" asset_txs: type: array description: An array of Tx hashes that included the given asset (latest first) diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 09b9eb37..7048ea0f 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -859,6 +859,26 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Information description: Get the information of an asset including first minting & token registry metadata + post: + tags: + - Asset + requestBody: + $ref: "#/components/requestBodies/asset_list" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata /asset_history: #RPC get: tags: @@ -1646,6 +1666,26 @@ components: _datum_hashes: - 818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46 - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501','424f4f4b'] + - ['1d7f33bd23d85e1a25d87d86fac4f199c3197a2f7afeb662a0f34e1e','776f726c646d6f62696c65746f6b656e'] securitySchemes: {} schemas: tip: @@ -2661,6 +2701,10 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + type: integer + description: Asset decimals + example: 6 quantity: type: string description: Asset quantity owned by account @@ -2844,6 +2888,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of assets on the UTxO @@ -2875,6 +2921,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3216,6 +3264,19 @@ components: creation_time: type: string $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_policy_list: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_supply: + $ref: "#/components/schemas/asset_info/items/properties/total_supply" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" asset_txs: type: array description: An array of Tx hashes that included the given asset (latest first) diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 78c6d942..f1ae044e 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -859,6 +859,26 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Information description: Get the information of an asset including first minting & token registry metadata + post: + tags: + - Asset + requestBody: + $ref: "#/components/requestBodies/asset_list" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata /asset_history: #RPC get: tags: @@ -1646,6 +1666,26 @@ components: _datum_hashes: - 5571e2c3549f15934a38382d1318707a86751fb70827f4cbd29b104480f1be9b - 5f7212f546d7e7308ce99b925f05538db19981f4ea3084559c0b28a363245826 + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e','7447454e53'] + - ['777e6b4903dab74963ae581d39875c5dac16c09bb1f511c0af1ddda8','6141414441'] securitySchemes: {} schemas: tip: @@ -2661,6 +2701,10 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + type: integer + description: Asset decimals + example: 6 quantity: type: string description: Asset quantity owned by account @@ -2844,6 +2888,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of assets on the UTxO @@ -2875,6 +2921,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3216,6 +3264,19 @@ components: creation_time: type: string $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_policy_list: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_supply: + $ref: "#/components/schemas/asset_info/items/properties/total_supply" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" asset_txs: type: array description: An array of Tx hashes that included the given asset (latest first) diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 59a06a50..223c2d30 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -859,6 +859,26 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Information description: Get the information of an asset including first minting & token registry metadata + post: + tags: + - Asset + requestBody: + $ref: "#/components/requestBodies/asset_list" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata /asset_history: #RPC get: tags: @@ -1646,6 +1666,26 @@ components: _datum_hashes: - 6181b3dc623cd8812caf027a3507e9b3095388a7cf3db65983e1fddd3a84c88c - f8ae55ff89e1f5366f23e16bcaf2073252337b96031a02d79e41d653b5f0a978 + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404','433374'] + - ['189e2c53985411addb8df0f3e09f70e343da69f06746c408aba672a8','15fc257714a51769e192761d674db2ee2e80137428e522f9b914debb5f785301'] securitySchemes: {} schemas: tip: @@ -2661,6 +2701,10 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + type: integer + description: Asset decimals + example: 6 quantity: type: string description: Asset quantity owned by account @@ -2844,6 +2888,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of assets on the UTxO @@ -2875,6 +2921,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3216,6 +3264,19 @@ components: creation_time: type: string $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_policy_list: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_supply: + $ref: "#/components/schemas/asset_info/items/properties/total_supply" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" asset_txs: type: array description: An array of Tx hashes that included the given asset (latest first) diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml index 7de592b3..ac737a6c 100644 --- a/specs/results/koiosapi-testnet.yaml +++ b/specs/results/koiosapi-testnet.yaml @@ -859,6 +859,26 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Information description: Get the information of an asset including first minting & token registry metadata + post: + tags: + - Asset + requestBody: + $ref: "#/components/requestBodies/asset_list" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata /asset_history: #RPC get: tags: @@ -1646,6 +1666,26 @@ components: _datum_hashes: - 5865d96e4313780a37af26055cdcc90308db23adaf4f2082178355e5e1dc20f6 - 4932dce28712ccc4858e3d83cc8e79b12740f66007dc9a287bb640a264c899de + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431'] + - ['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431'] securitySchemes: {} schemas: tip: @@ -2661,6 +2701,10 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + type: integer + description: Asset decimals + example: 6 quantity: type: string description: Asset quantity owned by account @@ -2844,6 +2888,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of assets on the UTxO @@ -2875,6 +2921,8 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3216,6 +3264,19 @@ components: creation_time: type: string $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_policy_list: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_supply: + $ref: "#/components/schemas/asset_info/items/properties/total_supply" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" asset_txs: type: array description: An array of Tx hashes that included the given asset (latest first) diff --git a/specs/templates/3-api-requestBodies.yaml b/specs/templates/3-api-requestBodies.yaml index 4bae7a8f..f0f25af0 100644 --- a/specs/templates/3-api-requestBodies.yaml +++ b/specs/templates/3-api-requestBodies.yaml @@ -198,4 +198,24 @@ requestBodies: example: _datum_hashes: - ##datum_hashes1_rb## - - ##datum_hashes2_rb## \ No newline at end of file + - ##datum_hashes2_rb## + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ##asset1_rb## + - ##asset2_rb## diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 7f3f549d..e4c681d9 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -1012,6 +1012,10 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + type: integer + description: Asset decimals + example: 6 quantity: type: string description: Asset quantity owned by account @@ -1195,6 +1199,8 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of assets on the UTxO @@ -1226,6 +1232,8 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -1567,6 +1575,19 @@ schemas: creation_time: type: string $ref: "#/components/schemas/asset_info/items/properties/creation_time" + asset_policy_list: + type: array + description: List of policy assets + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + total_supply: + $ref: "#/components/schemas/asset_info/items/properties/total_supply" + decimals: + $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" asset_txs: type: array description: An array of Tx hashes that included the given asset (latest first) diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index e5069d07..d2bb3c16 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -643,6 +643,26 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Information description: Get the information of an asset including first minting & token registry metadata + post: + tags: + - Asset + requestBody: + $ref: "#/components/requestBodies/asset_list" + responses: + "200": + description: Array of detailed asset information + content: + application/json: + schema: + $ref: "#/components/schemas/asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata /asset_history: #RPC get: tags: diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index b1fce30f..0fcbf095 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -93,6 +93,20 @@ "pv": "11", "pp": "30" }, + "asset1": { + "m": "['750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501','424f4f4b']", + "t": "['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431']", + "g": "['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e']", + "pv": "['065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404','433374']", + "pp": "['c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e','7447454e53']" + }, + "asset2": { + "m": "['1d7f33bd23d85e1a25d87d86fac4f199c3197a2f7afeb662a0f34e1e','776f726c646d6f62696c65746f6b656e']", + "t": "['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431']", + "g": "['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e']", + "pv": "['189e2c53985411addb8df0f3e09f70e343da69f06746c408aba672a8','15fc257714a51769e192761d674db2ee2e80137428e522f9b914debb5f785301']", + "pp": "['777e6b4903dab74963ae581d39875c5dac16c09bb1f511c0af1ddda8','6141414441']" + }, "stake_addresses1": { "m": "stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250", "t": "stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj", From adf93d25d193f221adbf9fc5a81dc81f011611e4 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Thu, 22 Dec 2022 10:55:58 +1100 Subject: [PATCH 34/86] Add a view for asset_token_registry (#145) Make block's parent/child hash strict Retire Legacy Testnet references Bump specs version --- files/grest/rpc/blocks/block_info.sql | 5 +- .../grest/rpc/views/asset_token_registry.sql | 15 + specs/createspecs.py | 1 - specs/results/koiosapi-guild.yaml | 47 +- specs/results/koiosapi-mainnet.yaml | 47 +- specs/results/koiosapi-preprod.yaml | 47 +- specs/results/koiosapi-preview.yaml | 47 +- specs/results/koiosapi-testnet.yaml | 3425 ----------------- specs/templates/1-api-info.yaml | 2 +- specs/templates/4-api-schemas.yaml | 23 + specs/templates/api-main.yaml | 22 +- specs/templates/example-map.json | 33 - topology/topology-testnet.json | 10 - 13 files changed, 242 insertions(+), 3482 deletions(-) create mode 100644 files/grest/rpc/views/asset_token_registry.sql delete mode 100644 specs/results/koiosapi-testnet.yaml delete mode 100644 topology/topology-testnet.json diff --git a/files/grest/rpc/blocks/block_info.sql b/files/grest/rpc/blocks/block_info.sql index f1d1f8dd..94de1bac 100644 --- a/files/grest/rpc/blocks/block_info.sql +++ b/files/grest/rpc/blocks/block_info.sql @@ -73,7 +73,7 @@ BEGIN FROM block tB WHERE - block_no = b.id - 1 + id = b.previous_id ) AS parent_hash, ( SELECT @@ -81,7 +81,7 @@ BEGIN FROM block tB WHERE - block_no = b.id + 1 + previous_id = b.id ) AS child_hash FROM block B @@ -110,4 +110,3 @@ END; $$; COMMENT ON FUNCTION grest.block_info IS 'Get detailed information about list of block hashes'; - diff --git a/files/grest/rpc/views/asset_token_registry.sql b/files/grest/rpc/views/asset_token_registry.sql new file mode 100644 index 00000000..70b01d89 --- /dev/null +++ b/files/grest/rpc/views/asset_token_registry.sql @@ -0,0 +1,15 @@ +CREATE VIEW grest.asset_token_registry AS + SELECT + asset_policy AS policy_id, + asset_name, + name AS asset_name_ascii, + ticker, + description, + url, + decimals, + logo + FROM + grest.asset_registry_cache +; + +COMMENT ON VIEW grest.asset_token_registry IS 'Get a list of assets registered via token registry on github'; diff --git a/specs/createspecs.py b/specs/createspecs.py index 4d32cd7b..a91d71bf 100755 --- a/specs/createspecs.py +++ b/specs/createspecs.py @@ -27,7 +27,6 @@ def populate_spec(network, outf): def main(): populate_spec("m", "results/koiosapi-mainnet.yaml") - populate_spec("t", "results/koiosapi-testnet.yaml") populate_spec("g", "results/koiosapi-guild.yaml") populate_spec("pv", "results/koiosapi-preview.yaml") populate_spec("pp", "results/koiosapi-preprod.yaml") diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 94306626..6cc910d4 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9 + version: 1.0.10rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -219,7 +219,8 @@ info: servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 - - url: https://testnet.koios.rest/api/v0 + - url: https://preview.koios.rest/api/v0 + - url: https://preprod.koios.rest/api/v0 paths: /tip: #RPC get: @@ -815,6 +816,25 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) + /asset_token_registry: + get: + tags: + - Asset + responses: + "200": + description: Array of token registry information for each asset + content: + application/json: + schema: + $ref: "#/components/schemas/asset_token_registry" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github /asset_address_list: #RPC get: tags: @@ -2406,6 +2426,7 @@ components: block_height: type: integer description: Block height + nullable: true example: 42325043 block_size: type: integer @@ -3108,6 +3129,28 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + asset_token_registry: + type: array + description: An array of token registry information (registered via github) for each asset + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + asset_name_ascii: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + ticker: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/ticker" + description: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/description" + url: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/url" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + logo: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 7048ea0f..d6b046e1 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9 + version: 1.0.10rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -219,7 +219,8 @@ info: servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 - - url: https://testnet.koios.rest/api/v0 + - url: https://preview.koios.rest/api/v0 + - url: https://preprod.koios.rest/api/v0 paths: /tip: #RPC get: @@ -815,6 +816,25 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) + /asset_token_registry: + get: + tags: + - Asset + responses: + "200": + description: Array of token registry information for each asset + content: + application/json: + schema: + $ref: "#/components/schemas/asset_token_registry" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github /asset_address_list: #RPC get: tags: @@ -2406,6 +2426,7 @@ components: block_height: type: integer description: Block height + nullable: true example: 42325043 block_size: type: integer @@ -3108,6 +3129,28 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + asset_token_registry: + type: array + description: An array of token registry information (registered via github) for each asset + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + asset_name_ascii: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + ticker: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/ticker" + description: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/description" + url: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/url" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + logo: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index f1ae044e..65798de9 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9 + version: 1.0.10rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -219,7 +219,8 @@ info: servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 - - url: https://testnet.koios.rest/api/v0 + - url: https://preview.koios.rest/api/v0 + - url: https://preprod.koios.rest/api/v0 paths: /tip: #RPC get: @@ -815,6 +816,25 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) + /asset_token_registry: + get: + tags: + - Asset + responses: + "200": + description: Array of token registry information for each asset + content: + application/json: + schema: + $ref: "#/components/schemas/asset_token_registry" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github /asset_address_list: #RPC get: tags: @@ -2406,6 +2426,7 @@ components: block_height: type: integer description: Block height + nullable: true example: 42325043 block_size: type: integer @@ -3108,6 +3129,28 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + asset_token_registry: + type: array + description: An array of token registry information (registered via github) for each asset + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + asset_name_ascii: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + ticker: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/ticker" + description: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/description" + url: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/url" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + logo: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 223c2d30..d08a50ee 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.9 + version: 1.0.10rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. @@ -219,7 +219,8 @@ info: servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 - - url: https://testnet.koios.rest/api/v0 + - url: https://preview.koios.rest/api/v0 + - url: https://preprod.koios.rest/api/v0 paths: /tip: #RPC get: @@ -815,6 +816,25 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) + /asset_token_registry: + get: + tags: + - Asset + responses: + "200": + description: Array of token registry information for each asset + content: + application/json: + schema: + $ref: "#/components/schemas/asset_token_registry" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github /asset_address_list: #RPC get: tags: @@ -2406,6 +2426,7 @@ components: block_height: type: integer description: Block height + nullable: true example: 42325043 block_size: type: integer @@ -3108,6 +3129,28 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + asset_token_registry: + type: array + description: An array of token registry information (registered via github) for each asset + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + asset_name_ascii: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + ticker: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/ticker" + description: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/description" + url: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/url" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + logo: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) diff --git a/specs/results/koiosapi-testnet.yaml b/specs/results/koiosapi-testnet.yaml deleted file mode 100644 index ac737a6c..00000000 --- a/specs/results/koiosapi-testnet.yaml +++ /dev/null @@ -1,3425 +0,0 @@ -openapi: 3.0.2 -info: - title: Koios API - version: 1.0.9 - description: | - Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. - - > Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). - - # Problems solved by Koios - - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) - to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is - free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be - health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. - - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be - consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute - or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that - pass the health-check for the endpoint. - - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For - such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that - take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part - of Koios API. - - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants - automatically receive failover support, they reduce impact of any subset of clusters going through update process. - - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to - give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. - - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will - ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. - - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. - - # Architecture - - ## How does Koios work? - - ![High-Level architecture overview](/koios-design.png) - - We will go bottom to top (from builder's eyes to run through the above) briefly: - - - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. - - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. - - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. - - # API Usage - - The endpoints served by Koios can be browsed from the left side bar of this site. You will find that almost each endpoint has an example that you can `Try` and will help you get an example in shell using cURL. For public queries, you do not need to register yourself - you can simply use them as per the examples provided on individual endpoints. But in addition, the [PostgREST API](https://postgrest.org/en/stable/api.html) used underneath provides a handful of features that can be quite handy for you to improve your queries to directly grab very specific information pertinent to your calls, reducing data you download and process. - - ## Vertical Filtering - - Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

- - ``` bash - curl "https://api.koios.rest/api/v0/tip" - - # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] - - curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" - - # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] - ``` - - ## Horizontal Filtering - - You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

- ``` bash - curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" - - # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, - # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] - ``` - - Here, we made use of `eq.` operator to denote a filter of "value equal to" against `epoch` column. Similarly, we added a filter using `lt.` operator to denote a filter of "values lower than" against `epoch_slot` column. You can find a complete list of operators supported in PostgREST documentation (commonly used ones extracted below): - - |Abbreviation|In PostgreSQL|Meaning | - |------------|-------------|-------------------------------------------| - |eq |`=` |equals | - |gt |`>` |greater than | - |gte |`>=` |greater than or equal | - |lt |`<` |less than | - |lte |`<=` |less than or equal | - |neq |`<>` or `!=` |not equal | - |like |`LIKE` |LIKE operator (use * in place of %) | - |in |`IN` |one of a list of values, e.g. `?a=in.("hi,there","yes,you")`| - |is |`IS` |checking for exact equality (null,true,false,unknown)| - |cs |`@>` |contains e.g. `?tags=cs.{example, new}` | - |cd |`<@` |contained in e.g. `?values=cd.{1,2,3}` | - |not |`NOT` |negates another operator | - |or |`OR` |logical `OR` operator | - |and |`AND` |logical `AND` operator | - - ## Pagination (offset/limit) - - When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. - - The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. - - Sounds confusing? Let's see this in practice, to hopefully make it easier. - Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

- - ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range - - # content-range: 0-999/* - - ``` - - As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

- - ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range - - # content-range: 1000-1499/* - - ``` - - There is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

- - ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range - - # content-range: 1000-1499/* - - ``` - - The above methods for pagination are very useful to keep your queries light as well as process the output in smaller pages, making better use of your resources and respecting server timeouts for response times. - - ## Ordering - - You can set a sorting order for returned queries against specific column(s). - Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

- - ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" - - # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, - # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, - # {"hash":"dc9496eae64294b46f07eb20499ae6dae4d81fdc67c63c354397db91bda1ee55","epoch":236,"abs_slot":16598058,"epoch_slot":9258,"block_height":5086962,"block_time":1608164349,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, - # {"hash":"6ebc7b734c513bc19290d96ca573a09cac9503c5a349dd9892b9ab43f917f9bd","epoch":236,"abs_slot":16601491,"epoch_slot":12691,"block_height":5087097,"block_time":1608167782,"tx_count":0,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, - # {"hash":"2eac97548829fc312858bc56a40f7ce3bf9b0ca27ee8530283ccebb3963de1c0","epoch":236,"abs_slot":16602308,"epoch_slot":13508,"block_height":5087136,"block_time":1608168599,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}] - ``` - - ## Response Formats - - You can get the results from the PostgREST endpoints in CSV or JSON formats. The default response format will always be JSON, but if you'd like to switch, you can do so by specifying header `'Accept: text/csv'` or `'Accept: application/json'`. - Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

- - ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" - - # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, - # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, - # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] - - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" - - # epoch,epoch_slot,block_time - # 318,28491,1643607582 - # 318,28479,1643607570 - # 318,28406,1643607497 - - ``` - - ## Limits - - While use of Koios is completely free and there are no registration requirements to the usage, the monitoring layer will only restrict spam requests that can potentially cause high amount of load to backends. The emphasis is on using list of objects first, and then [bulk where available] query specific objects to drill down where possible - which forms higher performance results to consumer as well as instance provider. Some basic protection against patterns that could cause unexpected resource spikes are protected as per below: - - - Burst Limit: A single IP can query an endpoint up to 100 times within 10 seconds (that's about 8.64 million requests within a day). The sleep time if a limit is crossed is minimal (60 seconds) for that IP - during which, the monitoring layer will return HTTP Status `429 - Too many requests`. - - Pagination/Limits: Any query results fetched will be paginated by 1000 records (you can reduce limit and or control pagination offsets on URL itself, see API > Pagination section for more details). - - Query timeout: If a query from server takes more than 30 seconds, it will return a HTTP Status of `504 - Gateway timeout`. This is because we would want to ensure you're using the queries optimally, and more often than not - it would indicate that particular endpoint is not optimised (or the network connectivity is not optimal between servers). - - Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. - - - # Community projects - - A big thank you to the following projects who are already starting to use Koios from early days: - - ## CLI - - - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) - - ## Libraries - - - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) - - [Go Client](https://github.com/cardano-community/koios-go-client) - - [Java Client](https://github.com/cardano-community/koios-java-client) - - ## Community Projects/Tools - - - [Building On Cardano](https://buildingoncardano.com) - - [CardaStat](cardastat.info) - - [CNFT.IO](https://cnft.io) - - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) - - [Dandelion](https://dandelion.link) - - [Eternl](https://eternl.io/) - - [PoolPeek](https://poolpeek.com) - - [TosiDrop](https://tosidrop.io) - - # FAQ - - ### Is there a price attached to using services? - For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). - - ### Who are the folks behind Koios? - It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) - who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many - for experiments. - - ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. - - ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). - - ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. - - x-logo: - url: "https://api.koios.rest/koios.png" -servers: - - url: https://api.koios.rest/api/v0 - - url: https://guild.koios.rest/api/v0 - - url: https://testnet.koios.rest/api/v0 -paths: - /tip: #RPC - get: - tags: - - Network - responses: - "200": - description: Array of block summary (limit+paginated) - content: - application/json: - schema: - $ref: "#/components/schemas/tip" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Query Chain Tip - description: Get the tip info about the latest block seen by chain - /genesis: #RPC - get: - tags: - - Network - responses: - "200": - description: Array of genesis parameters used to start each era on chain - content: - application/json: - schema: - $ref: "#/components/schemas/genesis" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Get Genesis info - description: Get the Genesis parameters used to start specific era on chain - /totals: #RPC - get: - tags: - - Network - parameters: - - $ref: "#/components/parameters/_epoch_no" - responses: - "200": - description: Array of supply/reserves/utxo/fees/treasury stats - content: - application/json: - schema: - $ref: "#/components/schemas/totals" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Get historical tokenomic stats - description: >- - Get the circulating utxo, treasury, rewards, supply and reserves in - lovelace for specified epoch, all epochs if empty - /epoch_info: #RPC - get: - tags: - - Epoch - parameters: - - $ref: "#/components/parameters/_epoch_no" - - $ref: "#/components/parameters/_include_next_epoch" - responses: - "200": - description: Array of detailed summary for each epoch - content: - application/json: - schema: - $ref: "#/components/schemas/epoch_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Epoch Information - description: Get the epoch information, all epochs if no epoch specified - /epoch_params: #RPC - get: - tags: - - Epoch - parameters: - - $ref: "#/components/parameters/_epoch_no" - responses: - "200": - description: Array of protocol parameters for each epoch - content: - application/json: - schema: - $ref: "#/components/schemas/epoch_params" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Epoch's Protocol Parameters - description: >- - Get the protocol parameters for specific epoch, returns information - about all epochs if no epoch specified - /epoch_block_protocols: #RPC - get: - tags: - - Epoch - parameters: - - $ref: "#/components/parameters/_epoch_no" - responses: - "200": - description: Array of distinct block protocol versions counts in epoch - content: - application/json: - schema: - $ref: "#/components/schemas/epoch_block_protocols" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Epoch's Block Protocols - description: >- - Get the information about block protocol distribution in epoch - /blocks: - get: - tags: - - Block - responses: - "200": - description: Array of block information - content: - application/json: - schema: - $ref: "#/components/schemas/blocks" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Block List - description: Get summarised details about all blocks (paginated - latest first) - /block_info: #RPC - post: - tags: - - Block - requestBody: - $ref: "#/components/requestBodies/block_hashes" - responses: - "200": - description: Array of detailed block information - content: - application/json: - schema: - $ref: "#/components/schemas/block_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Block Information - description: Get detailed information about a specific block - /block_txs: #RPC - post: - tags: - - Block - requestBody: - $ref: "#/components/requestBodies/block_hashes" - responses: - "200": - description: Array of transactions hashes - content: - application/json: - schema: - $ref: "#/components/schemas/block_txs" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Block Transactions - description: Get a list of all transactions included in provided blocks - /tx_info: #RPC - post: - tags: - - Transactions - requestBody: - $ref: "#/components/requestBodies/tx_ids" - responses: - "200": - description: Array of detailed information about transaction(s) - content: - application/json: - schema: - $ref: "#/components/schemas/tx_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Transaction Information - description: Get detailed information about transaction(s) - /tx_utxos: #RPC - post: - tags: - - Transactions - requestBody: - $ref: "#/components/requestBodies/tx_ids" - responses: - "200": - description: Array of inputs and outputs for given transaction(s) - content: - application/json: - schema: - $ref: "#/components/schemas/tx_utxos" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Transaction UTxOs - description: Get UTxO set (inputs/outputs) of transactions. - /tx_metadata: #RPC - post: - tags: - - Transactions - requestBody: - $ref: "#/components/requestBodies/tx_ids" - responses: - "200": - description: Array of metadata information present in each of the transactions queried - content: - application/json: - schema: - $ref: "#/components/schemas/tx_metadata" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Transaction Metadata - description: Get metadata information (if any) for given transaction(s) - /tx_metalabels: #RPC - get: - tags: - - Transactions - responses: - "200": - description: Array of known metadata labels - content: - application/json: - schema: - $ref: "#/components/schemas/tx_metalabels" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Transaction Metadata Labels - description: Get a list of all transaction metalabels - /submittx: #submit-api - post: - tags: - - Transactions - requestBody: - $ref: "#/components/requestBodies/txbin" - x-code-samples: - - lang: "Shell" - source: | - # Assuming ${data} is a raw binary serialized transaction on the file-system. - # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. - curl -X POST \ - --header "Content-Type: application/cbor" \ - --data-binary ${data} https://api.koios.rest/api/v0/submittx - responses: - "202": - description: OK - content: - application/json: - schema: - description: The transaction id. - type: string - format: hex - minLength: 64 - maxLength: 64 - example: 92bcd06b25dfbd89b578d536b4d3b7dd269b7c2aa206ed518012cffe0444d67f - "400": - description: An error occured while submitting transaction. - summary: Submit Transaction - description: Submit an already serialized transaction to the network. - /tx_status: #RPC - post: - tags: - - Transactions - requestBody: - $ref: "#/components/requestBodies/tx_ids" - responses: - "200": - description: Array of transaction confirmation counts - content: - application/json: - schema: - $ref: "#/components/schemas/tx_status" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Transaction Status (Block Confirmations) - description: Get the number of block confirmations for a given transaction hash list - /address_info: #RPC - post: - tags: - - Address - requestBody: - $ref: "#/components/requestBodies/payment_addresses" - responses: - "200": - description: Array of address information - content: - application/json: - schema: - $ref: "#/components/schemas/address_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Address Information - description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses - /address_txs: #RPC - post: - tags: - - Address - requestBody: - $ref: "#/components/requestBodies/address_txs" - responses: - "200": - description: Array of transaction hashes - content: - application/json: - schema: - $ref: "#/components/schemas/address_txs" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Address Transactions - description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) - /address_assets: #RPC - post: - tags: - - Address - requestBody: - $ref: "#/components/requestBodies/payment_addresses" - responses: - "200": - description: Array of address-owned assets - content: - application/json: - schema: - $ref: "#/components/schemas/address_assets" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for given addresses - /credential_txs: #RPC - post: - tags: - - Address - requestBody: - $ref: "#/components/requestBodies/credential_txs" - responses: - "200": - description: Array of transaction hashes - content: - application/json: - schema: - $ref: "#/components/schemas/credential_txs" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Transactions from payment credentials - description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) - /account_list: - get: - tags: - - Account - responses: - "200": - description: Array of account (stake address) IDs - content: - application/json: - schema: - $ref: "#/components/schemas/account_list" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account List - description: Get a list of all accounts - /account_info: #RPC - post: - tags: - - Account - requestBody: - $ref: "#/components/requestBodies/stake_addresses" - responses: - "200": - description: Array of account information - content: - application/json: - schema: - $ref: "#/components/schemas/account_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account Information - description: Get the account information for given stake addresses (accounts) - /account_info_cached: #RPC - post: - tags: - - Account - requestBody: - $ref: "#/components/requestBodies/stake_addresses" - responses: - "200": - description: Array of account information - content: - application/json: - schema: - $ref: "#/components/schemas/account_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account Information (Cached) - description: Get the cached account information for given stake addresses (accounts) - /account_rewards: #RPC - post: - tags: - - Account - requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" - responses: - "200": - description: Array of reward history information - content: - application/json: - schema: - $ref: "#/components/schemas/account_rewards" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account Rewards - description: >- - Get the full rewards history (including MIR) for given stake addresses (accounts) - /account_updates: #RPC - post: - tags: - - Account - requestBody: - $ref: "#/components/requestBodies/stake_addresses" - responses: - "200": - description: Array of account updates information - content: - application/json: - schema: - $ref: "#/components/schemas/account_updates" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account Updates - description: >- - Get the account updates (registration, deregistration, delegation and - withdrawals) for given stake addresses (accounts) - /account_addresses: #RPC - post: - tags: - - Account - requestBody: - $ref: "#/components/requestBodies/stake_addresses" - responses: - "200": - description: Array of payment addresses - content: - application/json: - schema: - $ref: "#/components/schemas/account_addresses" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account Addresses - description: Get all addresses associated with given staking accounts - /account_assets: #RPC - post: - tags: - - Account - requestBody: - $ref: "#/components/requestBodies/stake_addresses" - responses: - "200": - description: Array of assets owned by account - content: - application/json: - schema: - $ref: "#/components/schemas/account_assets" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account Assets - description: Get the native asset balance of given accounts - /account_history: #RPC - post: - tags: - - Account - requestBody: - $ref: "#/components/requestBodies/stake_addresses_with_epoch_no" - responses: - "200": - description: Array of active stake values per epoch - content: - application/json: - schema: - $ref: "#/components/schemas/account_history" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Account History - description: Get the staking history of given stake addresses (accounts) - /asset_list: - get: - tags: - - Asset - responses: - "200": - description: Array of policy IDs and asset names - content: - application/json: - schema: - $ref: "#/components/schemas/asset_list" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset List - description: Get the list of all native assets (paginated) - /asset_address_list: #RPC - get: - tags: - - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - responses: - "200": - description: Array of payment addresses holding the given token (including balances) - content: - application/json: - schema: - $ref: "#/components/schemas/asset_address_list" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset Address List - description: Get the list of all addresses holding a given asset - /asset_info: #RPC - get: - tags: - - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - responses: - "200": - description: Array of detailed asset information - content: - application/json: - schema: - $ref: "#/components/schemas/asset_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset Information - description: Get the information of an asset including first minting & token registry metadata - post: - tags: - - Asset - requestBody: - $ref: "#/components/requestBodies/asset_list" - responses: - "200": - description: Array of detailed asset information - content: - application/json: - schema: - $ref: "#/components/schemas/asset_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset Information (Bulk) - description: Get the information of a list of assets including first minting & token registry metadata - /asset_history: #RPC - get: - tags: - - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - responses: - "200": - description: Array of asset mint/burn history - content: - application/json: - schema: - $ref: "#/components/schemas/asset_history" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset History - description: Get the mint/burn history of an asset - /asset_policy_info: #RPC - get: - tags: - - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - responses: - "200": - description: Array of detailed information of assets under the same policy - content: - application/json: - schema: - $ref: "#/components/schemas/asset_policy_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset Policy Information - description: Get the information for all assets under the same policy - /asset_summary: #RPC - get: - tags: - - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - responses: - "200": - description: Array of asset summary information - content: - application/json: - schema: - $ref: "#/components/schemas/asset_summary" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset Summary - description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) - /asset_txs: #RPC - get: - tags: - - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - - $ref: "#/components/parameters/_after_block_height" - - $ref: "#/components/parameters/_history" - responses: - "200": - description: Array of Tx hashes that included the given asset - content: - application/json: - schema: - $ref: "#/components/schemas/asset_txs" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Asset Transactions - description: Get the list of current or all asset transaction hashes (newest first) - /pool_list: #RPC - get: - tags: - - Pool - responses: - "200": - description: Array of pool IDs and tickers - content: - application/json: - schema: - $ref: "#/components/schemas/pool_list" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool List - description: A list of all currently registered/retiring (not retired) pools - /pool_info: #RPC - post: - tags: - - Pool - requestBody: - $ref: "#/components/requestBodies/pool_ids" - responses: - "200": - description: Array of pool information - content: - application/json: - schema: - $ref: "#/components/schemas/pool_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Information - description: Current pool statuses and details for a specified list of pool ids - /pool_stake_snapshot: #RPC - get: - tags: - - Pool - parameters: - - $ref: "#/components/parameters/_pool_bech32" - responses: - "200": - description: Array of pool stake information for 3 snapshots - content: - application/json: - schema: - $ref: "#/components/schemas/pool_snapshot" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Stake Snapshot - description: Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation - /pool_delegators: #RPC - get: - tags: - - Pool - parameters: - - $ref: "#/components/parameters/_pool_bech32" - responses: - "200": - description: Array of pool delegator information - content: - application/json: - schema: - $ref: "#/components/schemas/pool_delegators" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Delegators List - description: Return information about live delegators for a given pool. - /pool_delegators_history: #RPC - get: - tags: - - Pool - parameters: - - $ref: "#/components/parameters/_pool_bech32" - - $ref: "#/components/parameters/_epoch_no" - responses: - "200": - description: Array of pool delegator information - content: - application/json: - schema: - $ref: "#/components/schemas/pool_delegators_history" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Delegators History - description: Return information about active delegators (incl. history) for a given pool and epoch number (all epochs if not specified). - /pool_blocks: #RPC - get: - tags: - - Pool - parameters: - - $ref: "#/components/parameters/_pool_bech32" - - $ref: "#/components/parameters/_epoch_no" - responses: - "200": - description: Array of blocks created by pool - content: - application/json: - schema: - $ref: "#/components/schemas/pool_blocks" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Blocks - description: >- - Return information about blocks minted by a given pool for all epochs - (or _epoch_no if provided) - /pool_history: #RPC - get: - tags: - - Pool - parameters: - - $ref: "#/components/parameters/_pool_bech32" - - $ref: "#/components/parameters/_epoch_no" - responses: - "200": - description: Array of pool history information - content: - application/json: - schema: - $ref: "#/components/schemas/pool_history_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Stake, Block and Reward History - description: >- - Return information about pool stake, block and reward history in a given epoch _epoch_no - (or all epochs that pool existed for, in descending order if no _epoch_no was provided) - /pool_updates: #RPC - get: - tags: - - Pool - parameters: - - $ref: "#/components/parameters/_pool_bech32_optional" - required: false - responses: - "200": - description: Array of historical pool updates - content: - application/json: - schema: - $ref: "#/components/schemas/pool_updates" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Updates (History) - description: Return all pool updates for all pools or only updates for specific pool if specified - /pool_relays: #RPC - get: - tags: - - Pool - responses: - "200": - description: Array of pool relay information - content: - application/json: - schema: - $ref: "#/components/schemas/pool_relays" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Relays - description: A list of registered relays for all currently registered/retiring (not retired) pools - /pool_metadata: #RPC - post: - tags: - - Pool - requestBody: - $ref: "#/components/requestBodies/pool_ids_optional" - responses: - "200": - description: Array of pool metadata - content: - application/json: - schema: - $ref: "#/components/schemas/pool_metadata" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Pool Metadata - description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools - /native_script_list: #RPC - get: - tags: - - Script - responses: - "200": - description: List of native script and creation tx hash pairs - content: - application/json: - schema: - $ref: "#/components/schemas/native_script_list" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Native Script List - description: List of all existing native script hashes along with their creation transaction hashes - /plutus_script_list: #RPC - get: - tags: - - Script - responses: - "200": - description: List of Plutus script and creation tx hash pairs - content: - application/json: - schema: - $ref: "#/components/schemas/plutus_script_list" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Plutus Script List - description: List of all existing Plutus script hashes along with their creation transaction hashes - /script_redeemers: #RPC - get: - tags: - - Script - parameters: - - $ref: "#/components/parameters/_script_hash" - responses: - "200": - description: List of all redeemers for a given script hash - content: - application/json: - schema: - $ref: "#/components/schemas/script_redeemers" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Script Redeemers - description: List of all redeemers for a given script hash - /datum_info: #RPC - post: - tags: - - Script - requestBody: - $ref: "#/components/requestBodies/datum_hashes" - responses: - "200": - description: List of datum information for given datum hashes - content: - application/json: - schema: - $ref: "#/components/schemas/datum_info" - "400": - $ref: "#/components/responses/BadRequest" - "401": - $ref: "#/components/responses/Unauthorized" - "404": - $ref: "#/components/responses/NotFound" - summary: Datum Information - description: List of datum information for given datum hashes -components: - parameters: - select: - name: select - description: Filtering Columns - schema: - type: string - in: query - required: false - on_conflict: - name: on_conflict - description: On Conflict - schema: - type: string - in: query - required: false - order: - name: order - description: Ordering - schema: - type: string - in: query - required: false - range: - name: Range - description: Limiting and Pagination - schema: - type: string - in: header - required: false - rangeUnit: - name: Range-Unit - description: Limiting and Pagination - schema: - default: items - type: string - in: header - required: false - offset: - name: offset - description: Limiting and Pagination - schema: - type: string - in: query - required: false - limit: - name: limit - description: Limiting and Pagination - schema: - type: string - in: query - required: false - example: 10 - _block_hash: - deprecated: false - name: _block_hash - description: Block Hash in hex format - schema: - type: string - example: f75fea40852ed7d7f539d008e45255725daef8553ae7162750836f279570813a - in: query - required: true - allowEmptyValue: false - _after_block_height: - deprecated: false - name: _after_block_height - description: Block height for specifying time delta - schema: - type: integer - example: 50000 - in: query - required: false - allowEmptyValue: true - _epoch_no: - deprecated: false - name: _epoch_no - description: Epoch Number to fetch details for - schema: - type: string - example: 185 - in: query - required: false - allowEmptyValue: true - _earned_epoch_no: - deprecated: false - name: _epoch_no - description: Filter for earned rewards Epoch Number - schema: - type: string - example: 185 - in: query - required: false - allowEmptyValue: true - _any_address: - deprecated: false - name: _address - description: Cardano payment or staking address in bech32 format - schema: - type: string - example: addr_test1qqn5x72yymml6aka0cmkew3jynqgld7xlnwtlsen9ln5tfll0dw5r75vk42mv3ykq8vyjeaanvpytg79xqzymqy5acmqqhx2n7 - in: query - required: true - allowEmptyValue: false - _address: - deprecated: false - name: _address - description: Cardano payment address in bech32 format - schema: - type: string - example: addr_test1qpqd4nqjepzdlh9zx5mx5ftnp4hecpgttcprtg0ur3ptpe9efftg48dy58fqwqwvatkn6pj877x858cr7peyr9466jmshglmne - in: query - required: true - allowEmptyValue: false - _address_assets: - deprecated: false - name: _address - description: Cardano payment address in bech32 format - schema: - type: string - example: addr_test1qzd9gtea50mqv60k3mq9txxtq2ynqwsxcnlx9ltvv3lh0rk9q2x2l6wv8fcr4wpgwmrcwhucsp80ycfw5ensx038hlfsp6lsxj - in: query - required: true - allowEmptyValue: false - _stake_address: - deprecated: false - name: _stake_address - description: Cardano staking address (reward account) in bech32 format - schema: - type: string - example: stake_test1uqqzl36c3vk850wk22yqgum9l0upy0y8588hcvsjq9m6j4cxw3qau - in: query - required: true - allowEmptyValue: false - _asset_policy: - deprecated: false - name: _asset_policy - description: Asset Policy ID in hexadecimal format (hex) - schema: - type: string - example: 000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b - in: query - required: true - allowEmptyValue: false - _asset_name: - deprecated: false - name: _asset_name - description: Asset Name in hexadecimal format (hex), empty asset name returns royalties - schema: - type: string - example: "54735465737431" - in: query - required: false - allowEmptyValue: true - _history: - deprecated: false - name: _history - description: Include all historical transactions, setting to false includes only the non-empty ones - schema: - type: boolean - example: "false" - in: query - required: false - allowEmptyValue: false - _include_next_epoch: - deprecated: false - name: _history - description: Include information about nearing but not yet started epoch, to get access to active stake snapshot information if available - schema: - type: boolean - example: "false" - in: query - required: false - allowEmptyValue: true - _pool_bech32: - deprecated: false - name: _pool_bech32 - description: Pool ID in bech32 format - schema: - type: string - example: pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh - in: query - required: true - allowEmptyValue: false - _pool_bech32_optional: - deprecated: false - name: _pool_bech32 - description: Pool ID in bech32 format (optional) - schema: - type: string - example: pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh - in: query - required: false - allowEmptyValue: true - _script_hash: - deprecated: false - name: _script_hash - description: Script hash in hexadecimal format (hex) - schema: - type: string - example: 9a3910acc1e1d49a25eb5798d987739a63f65eb48a78462ffae21e6f - in: query - required: true - allowEmptyValue: false - requestBodies: - block_hashes: - content: - application/json: - schema: - required: - - _block_hashes - type: object - properties: - _block_hashes: - format: text - type: array - items: - $ref: "#/components/schemas/blocks/items/properties/hash" - example: - _block_hashes: - - f75fea40852ed7d7f539d008e45255725daef8553ae7162750836f279570813a - - ff9f0c7fb1136de2cd6f10c9a140af2887f1d3614cc949bfeb262266d4c202b7 - - 5ef645ee519cde94a82f0aa880048c37978374f248f11e408ac0571a9054d9d3 - payment_addresses: - content: - application/json: - schema: - required: - - _addresses - type: object - properties: - _addresses: - format: text - type: array - items: - type: string - description: Array of Cardano payment address(es) in bech32 format - example: - _addresses: - - addr_test1qzx9hu8j4ah3auytk0mwcupd69hpc52t0cw39a65ndrah86djs784u92a3m5w475w3w35tyd6v3qumkze80j8a6h5tuqq5xe8y - - addr_test1qrk7920v35zukhcch4kyydy6rxnhqdcvetkvngeqrvtgavw8tpzdklse3kwer7urhrlfg962m9fc8cznfcdpka5pd07sgf8n0w - address_txs: - content: - application/json: - schema: - required: - - _addresses - type: object - properties: - _addresses: - format: text - type: array - items: - type: string - description: Array of Cardano payment address(es) in bech32 format - _after_block_height: - format: integer - type: integer - description: Only fetch information after specific block height - example: - _addresses: - - addr_test1qzx9hu8j4ah3auytk0mwcupd69hpc52t0cw39a65ndrah86djs784u92a3m5w475w3w35tyd6v3qumkze80j8a6h5tuqq5xe8y - - addr_test1qrk7920v35zukhcch4kyydy6rxnhqdcvetkvngeqrvtgavw8tpzdklse3kwer7urhrlfg962m9fc8cznfcdpka5pd07sgf8n0w - _after_block_height: 2342661 - stake_addresses_with_epoch_no: - content: - application/json: - schema: - required: - - _stake_addresses - type: object - properties: - _stake_addresses: - format: text - type: array - items: - type: string - description: Array of Cardano stake address(es) in bech32 format - _epoch_no: - format: integer - type: integer - description: Only fetch information for a specific epoch - example: - _stake_addresses: - - stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj - - stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx - _epoch_no: 200 - stake_addresses: - content: - application/json: - schema: - required: - - _stake_addresses - type: object - properties: - _stake_addresses: - format: text - type: array - items: - type: string - description: Array of Cardano stake address(es) in bech32 format - example: - _stake_addresses: - - stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj - - stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx - credential_txs: - content: - application/json: - schema: - required: - - _payment_credentials - type: object - properties: - _payment_credentials: - format: text - type: array - items: - type: string - description: Array of Cardano payment credential(s) in hex format - _after_block_height: - format: integer - type: integer - description: Only fetch information after specific block height - example: - _payment_credentials: - - 00003fac863dc2267d0cd90768c4af653572d719a79ca3b01957fa79 - - 000056d48603bf7daada30c9c175be9c93172d36f82fba0ca972c245 - _after_block_height: 2342661 - tx_ids: - content: - application/json: - schema: - required: - - _tx_hashes - type: object - properties: - _tx_hashes: - format: text - type: array - items: - type: string - description: Array of Cardano Transaction hashes - example: - _tx_hashes: - - 928052b80bfc23801da525a6bf8f805da36f22fa0fd5fec2198b0746eb82b72b - - c7e96e4cd6aa9e3afbc7b32d1e8023daf4197931f1ea61d2bdfc7a2e5e017cf1 - txbin: - content: - application/cbor: - schema: - type: string - format: binary - example: 928052b80bfc23801da525a6bf8f805da36f22fa0fd5fec2198b0746eb82b72b - pool_ids: - content: - application/json: - schema: - required: - - _pool_bech32_ids - type: object - properties: - _pool_bech32_ids: - format: text - type: array - items: - type: string - description: Array of Cardano pool IDs (bech32 format) - example: - _pool_bech32_ids: - - pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh - - pool102x86jz7uus6p6mlw02fdw2s805kng7g6ujs6s342t5msk36tch - - pool103qt58f9xlsr7y9anz3lnyq6cph4xh2yr4qrrtc356ldzz6ktqz - pool_ids_optional: - content: - application/json: - schema: - type: object - properties: - _pool_bech32_ids: - format: text - type: array - items: - type: string - description: Array of Cardano pool IDs (bech32 format) - example: - _pool_bech32_ids: - - pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh - - pool102x86jz7uus6p6mlw02fdw2s805kng7g6ujs6s342t5msk36tch - - pool103qt58f9xlsr7y9anz3lnyq6cph4xh2yr4qrrtc356ldzz6ktqz - datum_hashes: - content: - application/json: - schema: - type: object - properties: - _datum_hashes: - format: text - type: array - items: - type: string - description: Array of Cardano datum hashes - example: - _datum_hashes: - - 5865d96e4313780a37af26055cdcc90308db23adaf4f2082178355e5e1dc20f6 - - 4932dce28712ccc4858e3d83cc8e79b12740f66007dc9a287bb640a264c899de - asset_list: - content: - application/json: - schema: - required: - - _asset_list - type: object - properties: - _asset_list: - format: text - type: array - description: Array of array of policy ID and asset names (hex) - items: - type: array - items: - type: string - example: - _asset_list: - - ['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431'] - - ['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431'] - securitySchemes: {} - schemas: - tip: - type: array - items: - properties: - hash: - $ref: "#/components/schemas/blocks/items/properties/hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - abs_slot: - $ref: "#/components/schemas/blocks/items/properties/abs_slot" - epoch_slot: - $ref: "#/components/schemas/blocks/items/properties/epoch_slot" - block_no: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - genesis: - type: array - items: - properties: - networkmagic: - type: string - example: 764824073 - description: Unique network identifier for chain - networkid: - type: string - example: Mainnet - description: Network ID used at various CLI identification to distinguish between Mainnet and other networks - epochlength: - type: string - example: 432000 - description: Number of slots in an epoch - slotlength: - type: string - example: 1 - description: Duration of a single slot (in seconds) - maxlovelacesupply: - type: string - example: 45000000000000000 - description: Maximum smallest units (lovelaces) supply for the blockchain - systemstart: - type: integer - description: UNIX timestamp of the first block (genesis) on chain - example: 1506203091 - activeslotcoeff: - type: string - example: 0.05 - description: "Active Slot Co-Efficient (f) - determines the _probability_ of number of slots in epoch that are expected to have blocks (so mainnet, this would be: 432000 * 0.05 = 21600 estimated blocks)" - slotsperkesperiod: - type: string - example: 129600 - description: Number of slots that represent a single KES period (a unit used for validation of KES key evolutions) - maxkesrevolutions: - type: string - example: 62 - description: Number of KES key evolutions that will automatically occur before a KES (hot) key is expired. This parameter is for security of a pool, in case an operator had access to his hot(online) machine compromised - securityparam: - type: string - example: 2160 - description: A unit (k) used to divide epochs to determine stability window (used in security checks like ensuring atleast 1 block was created in 3*k/f period, or to finalize next epoch's nonce at 4*k/f slots before end of epoch) - updatequorum: - type: string - example: 5 - description: Number of BFT members that need to approve (via vote) a Protocol Update Proposal - alonzogenesis: - type: string - example: '{\"lovelacePerUTxOWord\":34482,\"executionPrices\":{\"prSteps\":{\"numerator\":721,\"denominator\":10000000},...' - description: A JSON dump of Alonzo Genesis - totals: - type: array - items: - properties: - epoch_no: - type: integer - description: Epoch number - example: 294 - circulation: - type: string - description: Circulating UTxOs for given epoch (in lovelaces) - example: 32081169442642320 - treasury: - type: string - description: Funds in treasury for given epoch (in lovelaces) - example: 637024173474141 - reward: - type: string - description: Rewards accumulated as of given epoch (in lovelaces) - example: 506871250479840 - supply: - type: string - description: Total Active Supply (sum of treasury funds, rewards, UTxOs, deposits and fees) for given epoch (in lovelaces) - example: 33228495612391330 - reserves: - type: string - description: Total Reserves yet to be unlocked on chain - example: 11771504387608670 - pool_list: - type: array - items: - properties: - pool_id_bech32: - type: string - nullable: true - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt - ticker: - type: string - nullable: true - description: Pool ticker - example: JAZZ - pool_history_info: - type: array - items: - type: object - properties: - epoch_no: - type: integer - description: Epoch for which the pool history data is shown - example: 312 - active_stake: - type: string - description: Amount of delegated stake to this pool at the time of epoch snapshot (in lovelaces) - example: "31235800000" - active_stake_pct: - type: number - description: Active stake for the pool, expressed as a percentage of total active stake on network - example: 13.512182543475783 - saturation_pct: - type: number - description: Saturation percentage of a pool at the time of snapshot (2 decimals) - example: 45.32 - block_cnt: - type: integer - nullable: true - description: Number of blocks pool created in that epoch - example: 14 - delegator_cnt: - type: integer - description: Number of delegators to the pool for that epoch snapshot - example: 1432 - margin: - type: number - description: Margin (decimal format) - example: 0.125 - fixed_cost: - type: string - description: Pool fixed cost per epoch (in lovelaces) - example: "340000000" - pool_fees: - type: string - description: Total amount of fees earned by pool owners in that epoch (in lovelaces) - example: "123327382" - deleg_rewards: - type: string - description: Total amount of rewards earned by delegators in that epoch (in lovelaces) - example: "123456789123" - epoch_ros: - type: number - description: Annualized ROS (return on staking) for delegators for this epoch - example: 3.000340466 - pool_info: - type: array - items: - type: object - properties: - pool_id_bech32: - type: string - description: Pool ID (bech32 format) - example: pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc - pool_id_hex: - type: string - description: Pool ID (Hex format) - example: a532904ca60e13e88437b58e7c6ff66b8d5e7ec8d3f4b9e4be7820ec - active_epoch_no: - $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" - vrf_key_hash: - type: string - description: Pool VRF key hash - example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 - margin: - type: number - description: Margin (decimal format) - example: 0.1 - fixed_cost: - type: string - description: Pool fixed cost per epoch - example: "500000000" - pledge: - type: string - description: Pool pledge in lovelace - example: "64000000000000" - reward_addr: - type: string - description: Pool reward address - example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 - owners: - type: array - items: - type: string - description: Pool (co)owner address - example: stake1u8088wvudd7dp3rxl0v9xgng8r3j50s65ge3l3jvgd94keqfm3nv3 - relays: - type: array - items: - type: object - properties: - dns: - type: string - nullable: true - description: DNS name of the relay (nullable) - example: relays-new.cardano-mainnet.iohk.io - srv: - type: string - nullable: true - description: DNS service name of the relay (nullable) - example: biostakingpool3.hopto.org - ipv4: - type: string - nullable: true - description: IPv4 address of the relay (nullable) - example: "54.220.20.40" - ipv6: - type: string - nullable: true - description: IPv6 address of the relay (nullable) - example: 2604:ed40:1000:1711:6082:78ff:fe0c:ebf - port: - type: number - nullable: true - description: Port number of the relay (nullable) - example: 6000 - meta_url: - type: string - description: Pool metadata URL - nullable: true - example: https://pools.iohk.io/IOGP.json - meta_hash: - type: string - description: Pool metadata hash - nullable: true - example: 37eb004c0dd8a221ac3598ca1c6d6257fb5207ae9857b7c163ae0f39259d6cc0 - meta_json: - type: object - nullable: true - properties: - name: - type: string - description: Pool name - example: Input Output Global (IOHK) - Private - ticker: - type: string - description: Pool ticker - example: IOGP - homepage: - type: string - description: Pool homepage URL - example: https://iohk.io - description: - type: string - description: Pool description - example: Our mission is to provide economic identity to the billions of people who lack it. IOHK will not use the IOHK ticker. - pool_status: - type: string - description: Pool status - enum: ["registered", "retiring", "retired"] - example: registered - retiring_epoch: - type: integer - description: Announced retiring epoch (nullable) - example: null - nullable: true - op_cert: - type: string - nullable: true - description: Pool latest operational certificate hash - example: 37eb004c0dd8a221ac3598ca1c6d6257fb5207ae9857b7c163ae0f39259d6cc0 - op_cert_counter: - type: integer - nullable: true - description: Pool latest operational certificate counter value - example: 8 - active_stake: - type: string - nullable: true - description: Pool active stake (will be null post epoch transition until dbsync calculation is complete) - example: "64328627680963" - sigma: - type: number - nullable: true - description: Pool relative active stake share - example: 0.0034839235 - block_count: - type: integer - nullable: true - description: Total pool blocks on chain - example: 4509 - live_pledge: - type: string - nullable: true - description: Summary of account balance for all pool owner's - example: "64328594406327" - live_stake: - type: string - nullable: true - description: Pool live stake - example: "64328627680963" - live_delegators: - type: integer - description: Pool live delegator count - example: 5 - live_saturation: - type: number - nullable: true - description: Pool live saturation (decimal format) - example: 94.52 - pool_snapshot: - type: array - nullable: true - items: - type: object - properties: - snapshot: - type: string - description: Type of snapshot ("Mark", "Set" or "Go") - example: "Mark" - epoch_no: - type: integer - description: Epoch number for the snapshot entry - example: 324 - nonce: - $ref: "#/components/schemas/epoch_params/items/properties/nonce" - pool_stake: - type: string - description: Pool's Active Stake for the given epoch - example: "100000000000" - active_stake: - type: string - description: Total Active Stake for the given epoch - example: "103703246364020" - pool_delegators: - type: array - nullable: true - items: - type: object - properties: - stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - amount: - type: string - description: Current delegator live stake (in lovelace) - example: 64328591517480 - active_epoch_no: - type: integer - description: Epoch number in which the delegation becomes active - example: 324 - latest_delegation_hash: - type: string - description: Latest transaction hash used for delegation by the account - example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 - pool_delegators_history: - type: array - nullable: true - items: - type: object - properties: - stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - amount: - $ref: "#/components/schemas/pool_delegators/items/properties/amount" - epoch_no: - type: integer - description: Epoch number for the delegation history - example: 324 - pool_blocks: - type: array - nullable: true - items: - type: object - properties: - epoch_no: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - epoch_slot: - $ref: "#/components/schemas/blocks/items/properties/epoch_slot" - abs_slot: - $ref: "#/components/schemas/blocks/items/properties/abs_slot" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_hash: - $ref: "#/components/schemas/blocks/items/properties/hash" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - pool_updates: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - pool_id_bech32: - $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" - pool_id_hex: - $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" - active_epoch_no: - type: integer - description: Epoch number in which the update becomes active - example: 324 - vrf_key_hash: - $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" - margin: - $ref: "#/components/schemas/pool_info/items/properties/margin" - fixed_cost: - $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" - pledge: - $ref: "#/components/schemas/pool_info/items/properties/pledge" - reward_addr: - $ref: "#/components/schemas/pool_info/items/properties/reward_addr" - owners: - $ref: "#/components/schemas/pool_info/items/properties/owners" - relays: - $ref: "#/components/schemas/pool_info/items/properties/relays" - meta_url: - $ref: "#/components/schemas/pool_info/items/properties/meta_url" - meta_hash: - $ref: "#/components/schemas/pool_info/items/properties/meta_hash" - meta_json: - $ref: "#/components/schemas/pool_info/items/properties/meta_json" - pool_status: - $ref: "#/components/schemas/pool_info/items/properties/pool_status" - retiring_epoch: - $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" - pool_relays: - type: array - items: - type: object - properties: - pool_id_bech32: - $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" - relays: - $ref: "#/components/schemas/pool_info/items/properties/relays" - pool_metadata: - type: array - items: - type: object - properties: - pool_id_bech32: - $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" - meta_url: - $ref: "#/components/schemas/pool_info/items/properties/meta_url" - meta_hash: - $ref: "#/components/schemas/pool_info/items/properties/meta_hash" - meta_json: - $ref: "#/components/schemas/pool_info/items/properties/meta_json" - epoch_info: - type: array - items: - type: object - properties: - epoch_no: - type: integer - description: Epoch number - example: 294 - out_sum: - type: string - description: Total output value across all transactions in epoch - example: 15432725054364942 - fees: - type: string - description: Total fees incurred by transactions in epoch - example: 74325855210 - tx_count: - type: integer - description: Number of transactions submitted in epoch - example: 357919 - blk_count: - type: integer - description: Number of blocks created in epoch - example: 17321 - start_time: - type: integer - description: UNIX timestamp of the epoch start - example: 1506203091 - end_time: - type: integer - description: UNIX timestamp of the epoch end - example: 1506635091 - first_block_time: - type: integer - description: UNIX timestamp of the epoch's first block - example: 1506635091 - last_block_time: - type: integer - description: UNIX timestamp of the epoch's last block - example: 1506635091 - active_stake: - type: string - description: Total active stake in epoch stake snapshot (null for pre-Shelley epochs) - example: 23395112387185880 - nullable: true - total_rewards: - type: string - description: Total rewards earned in epoch (null for pre-Shelley epochs) - example: 252902897534230 - nullable: true - avg_blk_reward: - type: string - description: Average block reward for epoch (null for pre-Shelley epochs) - example: 660233450 - nullable: true - epoch_params: - type: array - items: - properties: - epoch_no: - type: integer - description: Epoch number - example: 294 - min_fee_a: - type: integer - description: The 'a' parameter to calculate the minimum transaction fee - example: 44 - nullable: true - min_fee_b: - type: integer - description: The 'b' parameter to calculate the minimum transaction fee - example: 155381 - nullable: true - max_block_size: - type: integer - description: The maximum block size (in bytes) - example: 65536 - nullable: true - max_tx_size: - type: integer - description: The maximum transaction size (in bytes) - example: 16384 - nullable: true - max_bh_size: - type: integer - description: The maximum block header size (in bytes) - example: 1100 - nullable: true - key_deposit: - type: string - description: The amount (in lovelace) required for a deposit to register a stake address - example: 2000000 - nullable: true - pool_deposit: - type: string - description: The amount (in lovelace) required for a deposit to register a stake pool - example: 500000000 - nullable: true - max_epoch: - type: integer - description: The maximum number of epochs in the future that a pool retirement is allowed to be scheduled for - example: 18 - nullable: true - optimal_pool_count: - type: integer - description: The optimal number of stake pools - example: 500 - nullable: true - influence: - type: number - format: double - description: The pledge influence on pool rewards - example: 0.3 - nullable: true - monetary_expand_rate: - type: number - format: double - description: The monetary expansion rate - example: 0.003 - nullable: true - treasury_growth_rate: - type: number - format: double - description: The treasury growth rate - example: 0.2 - nullable: true - decentralisation: - type: number - format: double - description: The decentralisation parameter (1 fully centralised, 0 fully decentralised) - example: 0.1 - nullable: true - extra_entropy: - type: string - description: The hash of 32-byte string of extra random-ness added into the protocol's entropy pool - example: d982e06fd33e7440b43cefad529b7ecafbaa255e38178ad4189a37e4ce9bf1fa - nullable: true - protocol_major: - type: integer - description: The protocol major version - example: 5 - nullable: true - protocol_minor: - type: integer - description: The protocol minor version - example: 0 - nullable: true - min_utxo_value: - type: string - description: The minimum value of a UTxO entry - example: 34482 - nullable: true - min_pool_cost: - type: string - description: The minimum pool cost - example: 340000000 - nullable: true - nonce: - type: string - description: The nonce value for this epoch - example: 01304ddf5613166be96fce27be110747f2c8fcb38776618ee79225ccb59b81e2 - nullable: true - block_hash: - type: string - description: The hash of the first block where these parameters are valid - example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 - cost_models: - type: string - description: The per language cost models - example: null - nullable: true - price_mem: - type: number - format: double - description: The per word cost of script memory usage - example: 0.0577 - nullable: true - price_step: - type: number - format: double - description: The cost of script execution step usage - example: 7.21e-05 - nullable: true - max_tx_ex_mem: - type: number - description: The maximum number of execution memory allowed to be used in a single transaction - example: 10000000 - nullable: true - max_tx_ex_steps: - type: number - description: The maximum number of execution steps allowed to be used in a single transaction - example: 10000000000 - nullable: true - max_block_ex_mem: - type: number - description: The maximum number of execution memory allowed to be used in a single block - example: 50000000 - nullable: true - max_block_ex_steps: - type: number - description: The maximum number of execution steps allowed to be used in a single block - example: 40000000000 - nullable: true - max_val_size: - type: number - description: The maximum Val size - example: 5000 - nullable: true - collateral_percent: - type: integer - description: The percentage of the tx fee which must be provided as collateral when including non-native scripts - example: 150 - nullable: true - max_collateral_inputs: - type: integer - description: The maximum number of collateral inputs allowed in a transaction - example: 3 - nullable: true - coins_per_utxo_size: - type: string - description: The cost per UTxO size - example: 34482 - nullable: true - epoch_block_protocols: - type: array - items: - properties: - proto_major: - type: integer - description: Protocol major version - example: 6 - proto_minor: - type: integer - description: Protocol major version - example: 2 - blocks: - type: integer - description: Amount of blocks with specified major and protocol combination - example: 2183 - blocks: - type: array - items: - type: object - properties: - hash: - type: string - description: Hash of the block - example: e8c6992d52cd74b577b79251e0351be25070797a0dbc486b2c284d0bf7aeea9c - epoch_no: - type: integer - description: Epoch number of the block - example: 321 - abs_slot: - type: integer - description: Absolute slot number of the block - example: 53384242 - epoch_slot: - type: integer - description: Slot number of the block in epoch - example: 75442 - block_height: - type: integer - description: Block height - example: 42325043 - block_size: - type: integer - description: Block size in bytes - example: 79109 - block_time: - type: integer - description: UNIX timestamp of the block - example: 1506635091 - tx_count: - type: integer - description: Number of transactions in the block - example: 44 - vrf_key: - type: string - description: VRF key of the block producer - example: "vrf_vk1pmxyz8efuyj6eq6zkk373f28u47v06nwp5t59jr5fcmcusaazlmqhxu8k2" - pool: - type: string - description: Pool ID in bech32 format (null for pre-Shelley blocks) - example: pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc - nullable: true - op_cert_counter: - type: integer - description: Counter value of the operational certificate used to create this block - example: 8 - proto_major: - $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" - proto_minor: - $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" - block_info: - type: array - items: - type: object - properties: - hash: - $ref: "#/components/schemas/blocks/items/properties/hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - abs_slot: - $ref: "#/components/schemas/blocks/items/properties/abs_slot" - epoch_slot: - $ref: "#/components/schemas/blocks/items/properties/epoch_slot" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_size: - $ref: "#/components/schemas/blocks/items/properties/block_size" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - tx_count: - $ref: "#/components/schemas/blocks/items/properties/tx_count" - vrf_key: - $ref: "#/components/schemas/blocks/items/properties/vrf_key" - op_cert: - type: string - description: Hash of the block producers' operational certificate - example: "16bfc28a7127d11805fe02df67f8c3909ab7e2e2cd81b6954d90eeff1938614c" - op_cert_counter: - $ref: "#/components/schemas/blocks/items/properties/op_cert_counter" - pool: - $ref: "#/components/schemas/blocks/items/properties/pool" - proto_major: - $ref: "#/components/schemas/epoch_params/items/properties/protocol_major" - proto_minor: - $ref: "#/components/schemas/epoch_params/items/properties/protocol_minor" - total_output: - type: string - description: Total output of the block (in lovelace) - example: 92384672389 - nullable: true - total_fees: - type: string - description: Total fees of the block (in lovelace) - example: 2346834 - nullable: true - num_confirmations: - type: integer - description: Number of confirmations for the block - example: 664275 - parent_hash: - type: string - description: Hash of the parent of this block - example: "16bfc28a7127d11805fe02df67f8c3909ab7e2e2cd81b6954d90eeff1938614c" - child_hash: - type: string - description: Hash of the child of this block (if present) - example: "a3b525ba0747ce9daa928fa28fbc680f95e6927943a1fbd6fa5394d96c9dc2fa" - block_txs: - type: array - items: - type: object - properties: - block_hash: - $ref: "#/components/schemas/blocks/items/properties/hash" - tx_hashes: - type: array - items: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - address_info: - type: array - items: - type: object - properties: - address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" - balance: - type: string - description: Sum of all UTxO values beloning to address - example: 10723473983 - stake_address: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" - script_address: - type: boolean - description: Signifies whether the address is a script address - example: true - utxo_set: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" - tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - value: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" - datum_hash: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" - inline_datum: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" - reference_script: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" - asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" - address_txs: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - address_assets: - type: array - items: - type: object - properties: - address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" - asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" - credential_txs: - $ref: "#/components/schemas/address_txs" - account_list: - type: array - items: - type: object - properties: - id: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - account_info: - type: array - items: - type: object - properties: - stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - status: - type: string - description: Stake address status - enum: ["registered", "not registered"] - example: registered - delegated_pool: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" - total_balance: - type: string - description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) - example: 207116800428 - utxo: - type: string - description: Total UTxO balance of the account - example: 162764177131 - rewards: - type: string - description: Total rewards earned by the account - example: 56457728047 - withdrawals: - type: string - description: Total rewards withdrawn by the account - example: 12105104750 - rewards_available: - type: string - description: Total rewards available for withdawal - example: 44352623297 - reserves: - type: string - description: Total reserves MIR value of the account - example: "0" - treasury: - type: string - description: Total treasury MIR value of the account - example: "0" - account_rewards: - type: array - items: - type: object - properties: - stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - rewards: - type: array - items: - type: object - properties: - earned_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - spendable_epoch: - $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" - amount: - type: string - description: Amount of rewards earned (in lovelace) - type: - type: string - description: The source of the rewards - enum: [member, leader, treasury, reserves] - example: member - pool_id: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" - account_updates: - type: array - items: - type: object - properties: - stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - updates: - type: array - items: - type: object - properties: - action_type: - type: string - description: Type of certificate submitted - enum: ["registration", "delegation", "withdrawal", "deregistration"] - example: "registration" - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - epoch_slot: - $ref: "#/components/schemas/blocks/items/properties/epoch_slot" - absolute_slot: - $ref: "#/components/schemas/blocks/items/properties/abs_slot" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - account_addresses: - type: array - items: - type: object - properties: - stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - addresses: - type: array - items: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" - account_assets: - type: array - items: - type: object - properties: - stake_address: - $ref: "#/components/schemas/account_history/items/properties/stake_address" - asset_list: - type: array - items: - type: object - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - type: integer - description: Asset decimals - example: 6 - quantity: - type: string - description: Asset quantity owned by account - example: 990000 - account_history: - type: array - items: - properties: - stake_address: - type: string - description: Cardano staking address (reward account) in bech32 format - example: stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz - history: - type: array - items: - type: object - properties: - pool_id: - type: string - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt - epoch_no: - type: integer - description: Epoch number - example: 301 - active_stake: - type: string - description: Active stake amount (in lovelaces) - example: 682334162 - tx_info: - type: array - items: - type: object - properties: - tx_hash: - type: string - description: Hash identifier of the transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - block_hash: - $ref: "#/components/schemas/blocks/items/properties/hash" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - epoch_slot: - $ref: "#/components/schemas/blocks/items/properties/epoch_slot" - absolute_slot: - $ref: "#/components/schemas/blocks/items/properties/abs_slot" - tx_timestamp: - type: integer - description: UNIX timestamp of the transaction - example: 1506635091 - tx_block_index: - type: integer - description: Index of transaction within block - example: 6 - tx_size: - type: integer - description: Size in bytes of transaction - example: 391 - total_output: - type: string - description: Total sum of all transaction outputs (in lovelaces) - example: 157832856 - fee: - type: string - description: Total Transaction fee (in lovelaces) - example: 172761 - deposit: - type: string - description: Total Deposits included in transaction (for example, if it is registering a pool/key) - example: 0 - invalid_before: - type: string - description: Slot before which transaction cannot be validated (if supplied, else null) - nullable: true - invalid_after: - type: string - description: Slot after which transaction cannot be validated - example: 42332172 - nullable: true - collateral_inputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - description: An array of collateral inputs needed for smart contracts in case of contract failure - collateral_output: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items" - description: A collateral output for change if the smart contract fails to execute and collateral inputs are spent. (CIP-40) - nullable: true - reference_inputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - description: An array of reference inputs. A reference input allows looking at an output without spending it. (CIP-31) - inputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - description: An array of UTxO inputs spent in the transaction - outputs: - type: array - description: An array of UTxO outputs created by the transaction - items: - type: object - properties: - payment_addr: - type: object - properties: - bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw - cred: - type: string - description: Payment credential - example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 - stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" - tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 - value: - type: string - description: Total sum of ADA on the UTxO - example: 157832856 - datum_hash: - type: string - nullable: true - description: Hash of datum (if any) connected to UTxO - example: 30c16dd243324cf9d90ffcf211b9e0f2117a7dc28d17e85927dfe2af3328e5c9 - inline_datum: - type: object - nullable: true - description: Allows datums to be attached to UTxO (CIP-32) - properties: - bytes: - type: string - description: Datum bytes (hex) - example: 19029a - value: - type: object - description: Value (json) - example: { "int": 666 } - reference_script: - type: object - nullable: true - description: Allow reference scripts to be used to satisfy script requirements during validation, rather than requiring the spending transaction to do so. (CIP-33) - properties: - hash: - type: string - description: Hash of referenced script - example: 67f33146617a5e61936081db3b2117cbf59bd2123748f58ac9678656 - size: - type: integer - description: Size in bytes - example: 14 - type: - type: string - description: Type of script - example: plutusV1 - bytes: - type: string - description: Script bytes (hex) - example: 4e4d01000033222220051200120011 - value: - type: object - nullable: true - description: Value (json) - example: null - asset_list: - type: array - nullable: true - description: An array of assets on the UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - quantity: - type: string - description: Quantity of assets on the UTxO - example: 1 - withdrawals: - type: array - description: Array of withdrawals with-in a transaction - nullable: true - items: - type: object - properties: - amount: - type: string - description: Withdrawal amount (in lovelaces) - example: 9845162 - stake_addr: - type: string - description: A Cardano staking address (reward account, bech32 encoded) - example: stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj - assets_minted: - type: array - description: Array of minted assets with-in a transaction - nullable: true - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - quantity: - type: string - description: Quantity of minted assets (negative on burn) - example: 1 - metadata: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" - certificates: - type: array - nullable: true - description: Certificates present with-in a transaction (if any) - items: - properties: - index: - type: integer - nullable: true - description: Certificate index - example: 0 - type: - type: string - description: Type of certificate (could be delegation, stake_registration, stake_deregistraion, pool_update, pool_retire, param_proposal, reserve_MIR, treasury_MIR) - example: delegation - info: - type: object - description: A JSON array containing information from the certificate - nullable: true - example: - { - "stake_address": "stake1uxggf4shfvpghcangm67ky0q4zlc3xn7gezy0auhxczu3pslm9wrj", - "pool": "pool1k53pf4wzn263c08e3wr3gttndfecm9f4uzekgctcx947vt7fh2p", - } - native_scripts: - type: array - nullable: true - description: Native scripts present in a transaction (if any) - items: - properties: - script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" - script_json: - type: object - description: JSON representation of the timelock script (null for other script types) - example: - { - "type": "all", - "scripts": - [ - { - "type": "sig", - "keyHash": "a96da581c39549aeda81f539ac3940ac0cb53657e774ca7e68f15ed9", - }, - { - "type": "sig", - "keyHash": "ccfcb3fed004562be1354c837a4a4b9f4b1c2b6705229efeedd12d4d", - }, - { - "type": "sig", - "keyHash": "74fcd61aecebe36aa6b6cd4314027282fa4b41c3ce8af17d9b77d0d1", - }, - ], - } - plutus_contracts: - type: array - description: Plutus contracts present in transaction (if any) - items: - properties: - address: - type: string - description: Plutus script address - example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 - script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" - bytecode: - type: string - description: CBOR-encoded Plutus script data - example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 - size: - type: integer - description: The size of the CBOR serialised script (in bytes) - example: 234895 - valid_contract: - type: boolean - description: True if the contract is valid or there is no contract - example: true - input: - type: object - properties: - redeemer: - type: object - properties: - purpose: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/purpose" - fee: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/fee" - unit: - type: object - properties: - steps: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/unit_steps" - mem: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/unit_mem" - datum: - type: object - properties: - hash: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" - value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" - datum: - type: object - properties: - hash: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" - value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" - output: - type: object - nullable: true - properties: - hash: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" - value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" - tx_utxos: - type: array - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - inputs: - $ref: "#/components/schemas/tx_info/items/properties/inputs" - outputs: - $ref: "#/components/schemas/tx_info/items/properties/outputs" - tx_metadata: - type: array - nullable: true - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - metadata: - type: object - nullable: true - description: A JSON array containing details about metadata within transaction - example: - { - "721": - { - "version": 1, - "copyright": "...", - "publisher": ["p...o"], - "4bf184e01e0f163296ab253edd60774e2d34367d0e7b6cbc689b567d": - {}, - }, - } - tx_status: - type: array - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - num_confirmations: - type: integer - description: Number of block confirmations - example: 17 - nullable: true - tx_metalabels: - type: array - items: - properties: - key: - type: string - description: A distinct known metalabel - example: "721" - asset_list: - type: array - description: Array of policy IDs and asset names - items: - type: object - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - asset_address_list: - type: array - description: An array of payment addresses holding the given token (including balances) - items: - properties: - payment_address: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp - quantity: - type: string - description: Asset balance on the payment address - example: 23 - asset_summary: - type: array - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - total_transactions: - type: integer - description: Total number of transactions including the given asset - example: 89416 - staked_wallets: - type: integer - description: Total number of registered wallets holding the given asset - example: 548 - unstaked_addresses: - type: integer - description: Total number of payment addresses (not belonging to registered wallets) holding the given asset - example: 245 - asset_info: - type: array - items: - properties: - policy_id: - type: string - description: Asset Policy ID (hex) - example: d3501d9531fcc25e3ca4b6429318c2cc374dbdbcf5e99c1c1e5da1ff - asset_name: - type: string - nullable: true - description: Asset Name (hex) - example: 444f4e545350414d - asset_name_ascii: - type: string - description: Asset Name (ASCII) - example: DONTSPAM - fingerprint: - type: string - description: The CIP14 fingerprint of the asset - example: asset1ua6pz3yd5mdka946z8jw2fld3f8d0mmxt75gv9 - minting_tx_hash: - type: string - description: Hash of the latest mint transaction - example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 - mint_cnt: - type: integer - description: Count of total mint transactions - example: 1 - burn_cnt: - type: integer - description: Count of total burn transactions - example: 5 - minting_tx_metadata: - allOf: - - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" - description: Latest minting transaction metadata (aligns with CIP-25) - token_registry_metadata: - type: object - description: Asset metadata registered on the Cardano Token Registry - nullable: true - properties: - name: - type: string - example: Rackmob - description: - type: string - example: Metaverse Blockchain Cryptocurrency. - ticker: - type: string - example: MOB - url: - type: string - example: https://www.rackmob.com/ - logo: - type: string - description: A PNG image file as a byte string - example: iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAYAAACI7Fo9AAAACXBIWXMAAA7EAAAOxAGVKw4bAAADnmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc - decimals: - type: integer - example: 0 - total_supply: - type: string - example: "35000" - creation_time: - type: integer - description: UNIX timestamp of the first asset mint - example: 1506635091 - asset_history: - type: array - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - minting_txs: - type: array - description: Array of all mint/burn transactions for an asset - nullable: true - items: - type: object - properties: - tx_hash: - type: string - description: Hash of minting/burning transaction - example: e1ecc517f95715bb87681cfde2c594dbc971739f84f8bfda16170b35d63d0ddf - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - quantity: - type: string - description: Quantity minted/burned (negative numbers indicate burn transactions) - example: "-10" - metadata: - type: array - description: Array of Transaction Metadata for given transaction - items: - $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" - asset_policy_info: - type: array - description: List of policy assets - items: - properties: - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - asset_name_ascii: - $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - minting_tx_metadata: - $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" - token_registry_metadata: - $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" - total_supply: - type: string - example: "35000" - creation_time: - type: string - $ref: "#/components/schemas/asset_info/items/properties/creation_time" - asset_policy_list: - type: array - description: List of policy assets - items: - properties: - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - total_supply: - $ref: "#/components/schemas/asset_info/items/properties/total_supply" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - asset_txs: - type: array - description: An array of Tx hashes that included the given asset (latest first) - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - native_script_list: - type: array - items: - properties: - script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" - creation_tx_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" - type: - type: string - description: Type of the script - enum: ["timelock", "multisig"] - example: timelock - plutus_script_list: - type: array - items: - properties: - script_hash: - type: string - description: Hash of a script - example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 - creation_tx_hash: - type: string - description: Hash of the script creation transaction - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 - script_redeemers: - type: array - items: - type: object - properties: - script_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e - redeemers: - type: array - items: - type: object - properties: - tx_hash: - type: string - description: Hash of Transaction containing the redeemer - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 - tx_index: - type: integer - description: The index of the redeemer pointer in the transaction - example: 0 - unit_mem: - description: The budget in Memory to run a script - example: 520448 - nullable: true - additionalProperties: - oneOf: - - type: string - - type: integer - unit_steps: - description: The budget in Cpu steps to run a script - example: 211535239 - nullable: true - additionalProperties: - oneOf: - - type: string - - type: integer - fee: - type: string - description: The budget in fees to run a script - the fees depend on the ExUnits and the current prices - example: 45282 - purpose: - type: string - description: What kind of validation this redeemer is used for - enum: ["spend", "mint", "cert", "reward"] - example: spend - datum_hash: - type: string - description: The Hash of the Plutus Data - nullable: true - example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 - datum_value: - type: object - description: The actual data in json format - example: { "bytes": "3c33" } - datum_info: - type: array - items: - type: object - properties: - hash: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" - value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" - bytes: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" - headers: {} - responses: - OK: - description: Success! - NotFound: - description: The server does not recognise the combination of endpoint and parameters provided - Unauthorized: - description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint - PartialContent: - description: The result was truncated - BadRequest: - description: The server cannot process the request due to invalid input -tags: - - name: Network - description: Query information about the network - x-tag-expanded: false - - name: Epoch - description: Query epoch-specific details - x-tag-expanded: false - - name: Block - description: Query information about particular block on chain - x-tag-expanded: false - - name: Transactions - description: Query blockchain transaction details - x-tag-expanded: false - - name: Address - description: Query information about specific address(es) - x-tag-expanded: false - - name: Account - description: Query details about specific stake account addresses - x-tag-expanded: false - - name: Asset - description: Query Asset related informations - x-tag-expanded: false - - name: Pool - description: Query information about specific pools - x-tag-expanded: false - - name: Script - description: Query information about specific scripts (Smart Contracts) - x-tag-expanded: false -security: [] diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 78ced506..19a5fb6e 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -1,6 +1,6 @@ info: title: Koios API - version: 1.0.9 + version: 1.0.10rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index e4c681d9..9909537c 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -717,6 +717,7 @@ schemas: block_height: type: integer description: Block height + nullable: true example: 42325043 block_size: type: integer @@ -1419,6 +1420,28 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/asset_name" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + asset_token_registry: + type: array + description: An array of token registry information (registered via github) for each asset + items: + type: object + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + asset_name_ascii: + $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" + ticker: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/ticker" + description: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/description" + url: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/url" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + logo: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" asset_address_list: type: array description: An array of payment addresses holding the given token (including balances) diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index d2bb3c16..5d012f6f 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -3,7 +3,8 @@ openapi: 3.0.2 servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 - - url: https://testnet.koios.rest/api/v0 + - url: https://preview.koios.rest/api/v0 + - url: https://preprod.koios.rest/api/v0 paths: /tip: #RPC get: @@ -599,6 +600,25 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) + /asset_token_registry: + get: + tags: + - Asset + responses: + "200": + description: Array of token registry information for each asset + content: + application/json: + schema: + $ref: "#/components/schemas/asset_token_registry" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github /asset_address_list: #RPC get: tags: diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index 0fcbf095..95ae68be 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -2,84 +2,72 @@ "params": { "_block_hash": { "m": "f6192a1aaa6d3d05b4703891a6b66cd757801c61ace86cbe5ab0d66e07f601ab", - "t": "f75fea40852ed7d7f539d008e45255725daef8553ae7162750836f279570813a", "g": "bddbbc6df0ad09567a513349bafd56d8ec5c8fcd9ee9db12173624b896350d57", "pv": "6d6e3722c81a1b9bca9dd9b7f63cae096bd1f97f6ef1d67c178bacab1a248483", "pp": "021486aaa71f3c20c9fb26864fc8b00f7966a9007ff21e0753f16b9532450aa8" }, "_epoch_no": { "m": "320", - "t": "185", "g": "1950", "pv": "12", "pp": "31" }, "_earned_epoch_no": { "m": "320", - "t": "185", "g": "1950", "pv": "8", "pp": "23" }, "_any_address": { "m": "stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz", - "t": "addr_test1qqn5x72yymml6aka0cmkew3jynqgld7xlnwtlsen9ln5tfll0dw5r75vk42mv3ykq8vyjeaanvpytg79xqzymqy5acmqqhx2n7", "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2", "pv": "stake_test1uqd2nz8ugrn6kwkflvmt9he8dr966dszfmm5lt66qdmn28qt4wff9", "pp": "stake_test1uqc6cmh4um4t7x9p0nh5chlmf4txwhpw7572u8utuvgg80g47wc6x" }, "_address_assets": { "m": "addr1q8h22z0n3zqecr9n4q9ysds2m2ms3dqesz575accjpc3jclw55yl8zypnsxt82q2fqmq4k4hpz6pnq9fafm33yr3r93sgnpdw6", - "t": "addr_test1qzd9gtea50mqv60k3mq9txxtq2ynqwsxcnlx9ltvv3lh0rk9q2x2l6wv8fcr4wpgwmrcwhucsp80ycfw5ensx038hlfsp6lsxj", "g": "addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z", "pv": "addr_test1qphcrpt99pu2g2pwv32k8xqxa86pqswv2sa4tqefvruyfzeg3292mxuf3kq7nysjumlxjrlsfn9tp85r0l54l29x3qcsxxn580", "pp": "addr_test1qps7qvkjm6dxa6pnd65surma3qkpng02cc0xfyh6zjksge343f8yzpwq3av7gauswp5ec7nj2e5fxve0nptaknn5904s7zmvuh" }, "_address": { "m": "addr1qyp9kz50sh9c53hpmk3l4ewj9ur794t2hdqpngsjn3wkc5sztv9glpwt3frwrhdrltjaytc8ut2k4w6qrx3p98zad3fq07xe9g", - "t": "addr_test1qpqd4nqjepzdlh9zx5mx5ftnp4hecpgttcprtg0ur3ptpe9efftg48dy58fqwqwvatkn6pj877x858cr7peyr9466jmshglmne", "g": "addr_test1qqm3qvgq7wv7ywwgcwvwxscq3nclh06zwt3q09rhlzcdhx4lyk8g7246mrhwalx52nw0ntwcfml9wfgwkn22uqw82dcq7ct4gk", "pv": "addr_test1qzrxlwtmf4zzqndw6trwlgmyhnk8hncdx9w6ls9vvgy4xa0am5at75hv9zravxzwf5wlalxqm4jlr6u8qkp4t2unmdpsjvpe2n", "pp": "addr_test1qr33ynuc6yg4w56mccww3kcwqn54el84225xhvgkukf6wm32ah6gtt8ca4g0jdjcw50fk5ng5kpuc5uz4qula3gdlywssd3nq3" }, "_stake_address": { "m": "stake1u8yxtugdv63wxafy9d00nuz6hjyyp4qnggvc9a3vxh8yl0ckml2uz", - "t": "stake_test1uqqzl36c3vk850wk22yqgum9l0upy0y8588hcvsjq9m6j4cxw3qau", "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2", "pv": "stake_test1uzs5rxys8qy5jnr9g0mkj860ms5n92nrykmrgyumpf2ytmsejj4m6", "pp": "stake_test1urkzeud48zxwnjc54emzmmc3gkg2r6d6tm2sd799jxjnqxqlfzmvk" }, "_asset_policy": { "m": "750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501", - "t": "000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b", "g": "313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e", "pv": "065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404", "pp": "c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e" }, "_asset_name": { "m": "424f4f4b", - "t": "54735465737431", "g": "41484c636f696e", "pv": "433374", "pp": "7447454e53" }, "_pool_bech32": { "m": "pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc", - "t": "pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh", "g": "pool1xc9eywck4e20tydz4yvh5vfe0ep8whawvwz8wqkc9k046a2ypp4", "pv": "pool1leml52hm4fcp3hhe4zye08qz27llhj7d339p3gs0tl85cstx59q", "pp": "pool1tc3suyyxh9796rd92744mne82chxpmfc6mu7qgqzlx56sjfwvvl" }, "_pool_bech32_optional": { "m": "pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc", - "t": "pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh", "g": "pool1xc9eywck4e20tydz4yvh5vfe0ep8whawvwz8wqkc9k046a2ypp4", "pv": "pool1leml52hm4fcp3hhe4zye08qz27llhj7d339p3gs0tl85cstx59q", "pp": "pool1tc3suyyxh9796rd92744mne82chxpmfc6mu7qgqzlx56sjfwvvl" }, "_script_hash": { "m": "d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8", - "t": "9a3910acc1e1d49a25eb5798d987739a63f65eb48a78462ffae21e6f", "g": "160301a01ee86d8e46cbe3aef1e3bf69bfa28c65d5be2dde56a37af8", "pv": "f758cf422ca0cbed7d9d6fad1eb5a3c70537d62e661ad450dd2a3810", "pp": "590555d7b5760e98ae2bdd29b356247776251dfab0a207bfce98a485" @@ -88,147 +76,126 @@ "requestBodies": { "epoch_no": { "m": "350", - "t": "200", "g": "1500", "pv": "11", "pp": "30" }, "asset1": { "m": "['750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501','424f4f4b']", - "t": "['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431']", "g": "['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e']", "pv": "['065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404','433374']", "pp": "['c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e','7447454e53']" }, "asset2": { "m": "['1d7f33bd23d85e1a25d87d86fac4f199c3197a2f7afeb662a0f34e1e','776f726c646d6f62696c65746f6b656e']", - "t": "['000327a9e427a3a3256eb6212ae26b7f53f7969b8e62d37ea9138a7b','54735465737431']", "g": "['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e']", "pv": "['189e2c53985411addb8df0f3e09f70e343da69f06746c408aba672a8','15fc257714a51769e192761d674db2ee2e80137428e522f9b914debb5f785301']", "pp": "['777e6b4903dab74963ae581d39875c5dac16c09bb1f511c0af1ddda8','6141414441']" }, "stake_addresses1": { "m": "stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250", - "t": "stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj", "g": "stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2", "pv": "stake_test1upv7n2x0lxepkyx8ux2gjt74ecaa39tjgaccxl6hw5fwzngpzf5zt", "pp": "stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l" }, "stake_addresses2": { "m": "stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy", - "t": "stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx", "g": "stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd", "pv": "stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl", "pp": "stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx" }, "payment_addresses1": { "m": "addr1qy2jt0qpqz2z2z9zx5w4xemekkce7yderz53kjue53lpqv90lkfa9sgrfjuz6uvt4uqtrqhl2kj0a9lnr9ndzutx32gqleeckv", - "t": "addr_test1qzx9hu8j4ah3auytk0mwcupd69hpc52t0cw39a65ndrah86djs784u92a3m5w475w3w35tyd6v3qumkze80j8a6h5tuqq5xe8y", "g": "addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z", "pv": "addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc", "pp": "addr_test1vzpwq95z3xyum8vqndgdd9mdnmafh3djcxnc6jemlgdmswcve6tkw" }, "payment_addresses2": { "m": "addr1q9xvgr4ehvu5k5tmaly7ugpnvekpqvnxj8xy50pa7kyetlnhel389pa4rnq6fmkzwsaynmw0mnldhlmchn2sfd589fgsz9dd0y", - "t": "addr_test1qrk7920v35zukhcch4kyydy6rxnhqdcvetkvngeqrvtgavw8tpzdklse3kwer7urhrlfg962m9fc8cznfcdpka5pd07sgf8n0w", "g": "addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu", "pv": "addr_test1vqneq3v0dqh3x3muv6ee3lt8e5729xymnxuavx6tndcjc2cv24ef9", "pp": "addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc" }, "address_txs_after_block_height": { "m": "6238675", - "t": "2342661", "g": "120000", "pv": "40356", "pp": "9417" }, "block_info1": { "m": "fb9087c9f1408a7bbd7b022fd294ab565fec8dd3a8ef091567482722a1fa4e30", - "t": "f75fea40852ed7d7f539d008e45255725daef8553ae7162750836f279570813a", "g": "af2f6f7dd4e4ea6765103a1e38e023da3edd2b3c7fea2aa367222564dbe01cfd", "pv": "a4504e2495ed03b48be36676f430c54dca0769d29f72ebf18d493abf42d2167b", "pp": "2abeb8d1c1227139763be30ddb7a2fd79abd7d44195fca87a7c836a510b2802d" }, "block_info2": { "m": "60188a8dcb6db0d80628815be2cf626c4d17cb3e826cebfca84adaff93ad492a", - "t": "ff9f0c7fb1136de2cd6f10c9a140af2887f1d3614cc949bfeb262266d4c202b7", "g": "bddbbc6df0ad09567a513349bafd56d8ec5c8fcd9ee9db12173624b896350d57", "pv": "8e7a6206d2b21ae4f26e7e09353fadae17f838a63d095c2be51acbd16e9b7716", "pp": "4e790b758c495953bb33c4aad4a4b4c1b98f7c2ec135ebd3db21f32059481718" }, "block_info3": { "m": "c6646214a1f377aa461a0163c213fc6b86a559a2d6ebd647d54c4eb00aaab015", - "t": "5ef645ee519cde94a82f0aa880048c37978374f248f11e408ac0571a9054d9d3", "g": "732bf9bbc3780e6cd7ad57a3889dd5904f6e8a27d54eabf43eb0dbc485323f04", "pv": "1baaf7812ed48e663adb9eeaa68fe25034e5e30b4f8e56cc8600cac5e9d42ce7", "pp": "389da613316d2aec61edc34d51f1b3d004891ab38c9419771e5e0a3b12de3ef6" }, "credential_txs_payment_credentials1": { "m": "025b0a8f85cb8a46e1dda3fae5d22f07e2d56abb4019a2129c5d6c52", - "t": "00003fac863dc2267d0cd90768c4af653572d719a79ca3b01957fa79", "g": "b6b4b2b1e9e78369992aa5dd108aa4c3328fa228794ea9693400c2a0", "pv": "33c378cee41b2e15ac848f7f6f1d2f78155ab12d93b713de898d855f", "pp": "b429738bd6cc58b5c7932d001aa2bd05cfea47020a556c8c753d4436" }, "credential_txs_payment_credentials2": { "m": "13f6870c5e4f3b242463e4dc1f2f56b02a032d3797d933816f15e555", - "t": "000056d48603bf7daada30c9c175be9c93172d36f82fba0ca972c245", "g": "35e45387fc2acdd5cd641e339990e665ad4d9d065a41c94bc0b4be13", "pv": "52e63f22c5107ed776b70f7b92248b02552fd08f3e747bc745099441", "pp": "82e016828989cd9d809b50d6976d9efa9bc5b2c1a78d4b3bfa1bb83b" }, "tx_ids_tx_hashes1": { "m": "f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e", - "t": "928052b80bfc23801da525a6bf8f805da36f22fa0fd5fec2198b0746eb82b72b", "g": "bf04578d452dd3acb7c70fbac32dc972cb69f932f804171cfb4268f5af0228e7", "pv": "f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c", "pp": "d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530" }, "tx_ids_tx_hashes2": { "m": "0b8ba3bed976fa4913f19adc9f6dd9063138db5b4dd29cecde369456b5155e94", - "t": "c7e96e4cd6aa9e3afbc7b32d1e8023daf4197931f1ea61d2bdfc7a2e5e017cf1", "g": "63b716064012f858450731cb5f960c100c6cb639ec1ec999b898c604451f116a", "pv": "206f6da5b0b0de45605a95f5ce7e172be9674550f7dde3838c45cbf24bab8b00", "pp": "145688d3619e7524510ea64c0ec6363b77a9b8da179ef9bb0273a0940d57d576" }, "txbin": { "m": "f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e", - "t": "928052b80bfc23801da525a6bf8f805da36f22fa0fd5fec2198b0746eb82b72b", "g": "bf04578d452dd3acb7c70fbac32dc972cb69f932f804171cfb4268f5af0228e7", "pv": "f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c", "pp": "d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530" }, "pool_ids_pool_bech32_ids1": { "m": "pool100wj94uzf54vup2hdzk0afng4dhjaqggt7j434mtgm8v2gfvfgp", - "t": "pool102llj7e7a0mmxssjvjkv2d6lppuh6cz6q9xwc3tsksn0jqwz9eh", "g": "pool19st4a2vvu78tjtyywjte2eml3kx6ynersgd2nyw0y4jvyhlfu0u", "pv": "pool1p90428kec03mjdya3k4gv5d20w7lmed7ca0snknef5j977l3y8l", "pp": "pool1ext7qrwjzaxcdfhdnkq5mth59ukuu2atcg6tgqpmevpt7ratkta" }, "pool_ids_pool_bech32_ids2": { "m": "pool102s2nqtea2hf5q0s4amj0evysmfnhrn4apyyhd4azcmsclzm96m", - "t": "pool102x86jz7uus6p6mlw02fdw2s805kng7g6ujs6s342t5msk36tch", "g": "pool1uul8pytp2p0xq4ckjn3l294km0m7fuef46teehvh3x5tk46sfx3", "pv": "pool1wwh3k3ldzujdvgxllfwlnnkxyheafkacqlufnvpr77n5q72f9hw", "pp": "pool1x4p3cwemsm356vpxnjwuud7w76jz64hyss729zp7xa6wuey6yr9" }, "pool_ids_pool_bech32_ids3": { "m": "pool102vsulhfx8ua2j9fwl2u7gv57fhhutc3tp6juzaefgrn7ae35wm", - "t": "pool103qt58f9xlsr7y9anz3lnyq6cph4xh2yr4qrrtc356ldzz6ktqz", "g": "pool1us9ww725p0vygae5zaydme3apt7wg9se2yemhl8mgwkes3tlrqp", "pv": "pool1p835jxsj8py5n34lrgk6fvpgpxxvh585qm8dzvp7ups37vdet5a", "pp": "pool1ws42l6rawqjv58crs5l32v0eem3qnngpnjfd7epwd4lmjccc5cg" }, "datum_hashes1": { "m": "818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46", - "t": "5865d96e4313780a37af26055cdcc90308db23adaf4f2082178355e5e1dc20f6", "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", "pv": "6181b3dc623cd8812caf027a3507e9b3095388a7cf3db65983e1fddd3a84c88c", "pp": "5571e2c3549f15934a38382d1318707a86751fb70827f4cbd29b104480f1be9b" }, "datum_hashes2": { "m": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", - "t": "4932dce28712ccc4858e3d83cc8e79b12740f66007dc9a287bb640a264c899de", "g": "45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", "pv": "f8ae55ff89e1f5366f23e16bcaf2073252337b96031a02d79e41d653b5f0a978", "pp": "5f7212f546d7e7308ce99b925f05538db19981f4ea3084559c0b28a363245826" diff --git a/topology/topology-testnet.json b/topology/topology-testnet.json deleted file mode 100644 index 55215acf..00000000 --- a/topology/topology-testnet.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Providers": [ - {"name":"olassl","addr":"koios-testnet.ahlnet.nu","port":2155,"ssl":"true"}, - {"name":"damjan","addr":"195.201.129.190","port":8053}, - {"name":"homer","addr":"95.216.173.194","port":8053}, - {"name":"rdlrt","addr":"89.58.43.194","port":8053}, - {"name":"reqlez","addr":"192.234.196.167","port":8053} - ], - "Consumers": [] -} From 87868f518a560045d3233b5d0e81a0fbee8abfab Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Fri, 6 Jan 2023 23:06:04 +1100 Subject: [PATCH 35/86] Add workflow to sync repo (#150) --- .github/workflows/main.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..89191c07 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,24 @@ +name: Update Repository + +on: [ push, pull_request ] + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + token: ${{ secrets.GIT_CC }} + - name: Clone repository + run: | + git clone https://github.com/cardano-community/koios-artifacts.git + cd koios-artifacts + git config --global user.name 'cardano-bot' + git config --global user.email '${{ secrets.GIT_EMAIL }}' + - name: Sync repositories + run: | + git remote add new https://github.com/koios-official/koios-artifacts.git + git fetch --unshallow origin + git fetch --all + git remote set-url --add --push new https://github.com/koios-official/koios-artifacts.git + git push -f --all new From f9fabf4bee99d53624688ddbc5e784b50d34a450 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Fri, 13 Jan 2023 09:31:41 +1100 Subject: [PATCH 36/86] Update projects list (#153) --- projects.json | 52 ++++++++++++++++++++++++----- specs/results/koiosapi-guild.yaml | 23 +------------ specs/results/koiosapi-mainnet.yaml | 23 +------------ specs/results/koiosapi-preprod.yaml | 23 +------------ specs/results/koiosapi-preview.yaml | 23 +------------ specs/templates/1-api-info.yaml | 23 +------------ 6 files changed, 49 insertions(+), 118 deletions(-) diff --git a/projects.json b/projects.json index d949d856..dd01b20e 100644 --- a/projects.json +++ b/projects.json @@ -3,6 +3,10 @@ { "text": "Koios CLI in GoLang", "link": "https://github.com/cardano-community/koios-cli" + }, + { + "text": "CardanoSharp CSCLI", + "link": "https://github.com/CardanoSharp/cscli" } ], "Libraries": [ @@ -15,15 +19,27 @@ "link": "https://github.com/cardano-community/koios-go-client" }, { - "text": "Java client", + "text": "Java Client", "link": "https://github.com/cardano-community/koios-java-client" }, { - "text": "Python Client", + "text": "JavaScript Client", + "link": "https://meshjs.dev/providers/koios" + }, + { + "text": "Python Library - Quixote", "link": "https://github.com/cardano-community/koios-python" + }, + { + "text": "Python Library - Apex", + "link": "https://github.com/cardano-apexpool/koios-api-python" } ], "Community Projects/Tools": [ + { + "text": "ADABox", + "link": "https://explorer.adabox.io/pools" + }, { "text": "Building On Cardano", "link": "https://buildingoncardano.com" @@ -38,11 +54,11 @@ }, { "text": "CNTools", - "link": "https://cardano-community.github.io/guild-operators/Scripts/cntools/" - }, - { - "text": "cscli", - "link": "https://github.com/CardanoSharp/cscli" + "link": "https://cardano-community.github.io/guild-operators/Scripts/cntools/", + "logo": { + "dark": "https://github.com/cardano-community/guild-operators/raw/alpha/guild.png", + "light": "https://github.com/cardano-community/guild-operators/raw/alpha/guild.png" + } }, { "text": "Dandelion", @@ -52,13 +68,33 @@ "text": "Eternl", "link": "https://eternl.io/" }, + { + "text": "Mesh SDK", + "link": "https://meshjs.dev", + "logo": { + "dark": "https://meshjs.dev/logo-mesh/black/logo-mesh-black-256x256.png", + "light": "https://meshjs.dev/logo-mesh/white/logo-mesh-white-256x256.png" + } + }, + { + "text": "MusicBox", + "link": "https://musicboxnft.com", + "logo": { + "dark": "https://github.com/adabox-aio/musicbox-website/blob/main/src/main/resources/static/favicon/android-icon-192x192.png", + "light": "https://github.com/adabox-aio/musicbox-website/blob/main/src/main/resources/static/favicon/android-icon-192x192.png" + } + }, { "text": "Poolpeek", "link": "https://poolpeek.com" }, { "text": "TosiDrop", - "link": "https://tosidrop.io" + "link": "https://tosidrop.io", + "logo": { + "dark": "https://www.tosidrop.io/img/umbrella_blue.png", + "light": "https://www.tosidrop.io/img/umbrella_blue.png" + } }, { "text": "Raw Cardano Explorer", diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 6cc910d4..5f9310bf 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -172,28 +172,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days: - - ## CLI - - - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) - - ## Libraries - - - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) - - [Go Client](https://github.com/cardano-community/koios-go-client) - - [Java Client](https://github.com/cardano-community/koios-java-client) - - ## Community Projects/Tools - - - [Building On Cardano](https://buildingoncardano.com) - - [CardaStat](cardastat.info) - - [CNFT.IO](https://cnft.io) - - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) - - [Dandelion](https://dandelion.link) - - [Eternl](https://eternl.io/) - - [PoolPeek](https://poolpeek.com) - - [TosiDrop](https://tosidrop.io) + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) # FAQ diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index d6b046e1..7128c9b6 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -172,28 +172,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days: - - ## CLI - - - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) - - ## Libraries - - - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) - - [Go Client](https://github.com/cardano-community/koios-go-client) - - [Java Client](https://github.com/cardano-community/koios-java-client) - - ## Community Projects/Tools - - - [Building On Cardano](https://buildingoncardano.com) - - [CardaStat](cardastat.info) - - [CNFT.IO](https://cnft.io) - - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) - - [Dandelion](https://dandelion.link) - - [Eternl](https://eternl.io/) - - [PoolPeek](https://poolpeek.com) - - [TosiDrop](https://tosidrop.io) + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) # FAQ diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 65798de9..2f78bc43 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -172,28 +172,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days: - - ## CLI - - - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) - - ## Libraries - - - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) - - [Go Client](https://github.com/cardano-community/koios-go-client) - - [Java Client](https://github.com/cardano-community/koios-java-client) - - ## Community Projects/Tools - - - [Building On Cardano](https://buildingoncardano.com) - - [CardaStat](cardastat.info) - - [CNFT.IO](https://cnft.io) - - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) - - [Dandelion](https://dandelion.link) - - [Eternl](https://eternl.io/) - - [PoolPeek](https://poolpeek.com) - - [TosiDrop](https://tosidrop.io) + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) # FAQ diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index d08a50ee..1484a329 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -172,28 +172,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days: - - ## CLI - - - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) - - ## Libraries - - - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) - - [Go Client](https://github.com/cardano-community/koios-go-client) - - [Java Client](https://github.com/cardano-community/koios-java-client) - - ## Community Projects/Tools - - - [Building On Cardano](https://buildingoncardano.com) - - [CardaStat](cardastat.info) - - [CNFT.IO](https://cnft.io) - - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) - - [Dandelion](https://dandelion.link) - - [Eternl](https://eternl.io/) - - [PoolPeek](https://poolpeek.com) - - [TosiDrop](https://tosidrop.io) + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) # FAQ diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 19a5fb6e..eb7dd7bd 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -171,28 +171,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days: - - ## CLI - - - [Koios CLI in GoLang](https://github.com/cardano-community/koios-cli) - - ## Libraries - - - [.Net SDK](https://github.com/CardanoSharp/cardanosharp-koios) - - [Go Client](https://github.com/cardano-community/koios-go-client) - - [Java Client](https://github.com/cardano-community/koios-java-client) - - ## Community Projects/Tools - - - [Building On Cardano](https://buildingoncardano.com) - - [CardaStat](cardastat.info) - - [CNFT.IO](https://cnft.io) - - [CNTools](https://cardano-community.github.io/guild-operators/Scripts/cntools/) - - [Dandelion](https://dandelion.link) - - [Eternl](https://eternl.io/) - - [PoolPeek](https://poolpeek.com) - - [TosiDrop](https://tosidrop.io) + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) # FAQ From eed23f862af8ed0898d99577fdef68e56400fb93 Mon Sep 17 00:00:00 2001 From: "Ola [AHLNET]" Date: Thu, 19 Jan 2023 08:32:12 +0100 Subject: [PATCH 37/86] Account & Asset addresses update (#149) * Account & Asset addresses update - account_addresses: new flags: - _first_only: return first address only - _empty: return also used but empty addresses (if first only is not set) - asset_addresses: - asset_address_list renamed to asset_addresses to be in line with other endpoints. - asset_address_list kept as endpoint but marked as deprecated in API. Internally calls asset_addresses - asset_nft_address: new endpoint that returns address specified NFT sits on. closes #147 #148 Co-authored-by: rdlrt <3169068+rdlrt@users.noreply.github.com> --- files/grest/rpc/account/account_addresses.sql | 77 ++++-- files/grest/rpc/account/account_assets.sql | 2 +- files/grest/rpc/account/account_info.sql | 2 +- files/grest/rpc/address/address_assets.sql | 2 +- files/grest/rpc/address/credential_utxos.sql | 35 +++ files/grest/rpc/assets/asset_address_list.sql | 42 --- files/grest/rpc/assets/asset_addresses.sql | 59 +++++ files/grest/rpc/assets/asset_info_bulk.sql | 2 +- files/grest/rpc/assets/asset_nft_address.sql | 40 +++ .../rpc/assets/policy_asset_addresses.sql | 48 ++++ ..._policy_info.sql => policy_asset_info.sql} | 41 ++- ..._policy_list.sql => policy_asset_list.sql} | 4 +- files/grest/rpc/epoch/epoch_info.sql | 6 +- files/grest/rpc/epoch/epoch_params.sql | 2 + specs/results/koiosapi-guild.yaml | 244 ++++++++++++++++-- specs/results/koiosapi-mainnet.yaml | 244 ++++++++++++++++-- specs/results/koiosapi-preprod.yaml | 244 ++++++++++++++++-- specs/results/koiosapi-preview.yaml | 244 ++++++++++++++++-- specs/templates/2-api-params.yaml | 42 ++- specs/templates/3-api-requestBodies.yaml | 26 ++ specs/templates/4-api-schemas.yaml | 53 ++-- specs/templates/api-main.yaml | 123 ++++++++- specs/templates/example-map.json | 12 + 23 files changed, 1384 insertions(+), 210 deletions(-) create mode 100644 files/grest/rpc/address/credential_utxos.sql delete mode 100644 files/grest/rpc/assets/asset_address_list.sql create mode 100644 files/grest/rpc/assets/asset_addresses.sql create mode 100644 files/grest/rpc/assets/asset_nft_address.sql create mode 100644 files/grest/rpc/assets/policy_asset_addresses.sql rename files/grest/rpc/assets/{asset_policy_info.sql => policy_asset_info.sql} (61%) rename files/grest/rpc/assets/{asset_policy_list.sql => policy_asset_list.sql} (86%) diff --git a/files/grest/rpc/account/account_addresses.sql b/files/grest/rpc/account/account_addresses.sql index 070730b8..b46e04ce 100644 --- a/files/grest/rpc/account/account_addresses.sql +++ b/files/grest/rpc/account/account_addresses.sql @@ -1,4 +1,4 @@ -CREATE FUNCTION grest.account_addresses (_stake_addresses text[]) +CREATE OR REPLACE FUNCTION grest.account_addresses (_stake_addresses text[], _first_only boolean default false, _empty boolean default false) RETURNS TABLE ( stake_address varchar, addresses json @@ -15,26 +15,65 @@ BEGIN WHERE STAKE_ADDRESS.VIEW = ANY(_stake_addresses); - RETURN QUERY - WITH txo_addr AS ( - SELECT address, stake_address_id FROM - (SELECT - DISTINCT ON (address) address, stake_address_id, id + IF _first_only IS NOT TRUE AND _empty IS NOT TRUE THEN + RETURN QUERY + WITH txo_addr AS ( + SELECT + DISTINCT ON (address) address, + stake_address_id FROM - TX_OUT - WHERE stake_address_id = ANY(sa_id_list) ORDER BY address, id) x - ORDER BY id - ) - SELECT - sa.view as stake_address, - JSON_AGG(txo_addr.address) as addresses - FROM - txo_addr - INNER JOIN STAKE_ADDRESS sa ON sa.id = txo_addr.stake_address_id - GROUP BY - sa.id; + ( + SELECT + txo.address, + txo.stake_address_id, + txo.id + FROM + tx_out txo + LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id + AND txo.index::smallint = tx_in.tx_out_index::smallint + WHERE + txo.stake_address_id = ANY(sa_id_list) + AND tx_in.id IS NULL + ) x + ) + SELECT + sa.view as stake_address, + JSON_AGG(txo_addr.address) as addresses + FROM + txo_addr + INNER JOIN STAKE_ADDRESS sa ON sa.id = txo_addr.stake_address_id + GROUP BY + sa.id; + ELSE + RETURN QUERY + WITH txo_addr AS ( + SELECT + DISTINCT ON (address) address, + stake_address_id + FROM + ( + SELECT + txo.address, + txo.stake_address_id, + txo.id + FROM + tx_out txo + WHERE + txo.stake_address_id = ANY(sa_id_list) + LIMIT (CASE WHEN _first_only IS TRUE THEN 1 ELSE NULL END) + ) x + ) + SELECT + sa.view as stake_address, + JSON_AGG(txo_addr.address) as addresses + FROM + txo_addr + INNER JOIN STAKE_ADDRESS sa ON sa.id = txo_addr.stake_address_id + GROUP BY + sa.id; + END IF; END; $$; -COMMENT ON FUNCTION grest.account_addresses IS 'Get all addresses associated with given accounts'; +COMMENT ON FUNCTION grest.account_addresses IS 'Get all addresses associated with given accounts, optionally filtered by first used address only or inclusion of used but empty(no utxo) addresses.'; diff --git a/files/grest/rpc/account/account_assets.sql b/files/grest/rpc/account/account_assets.sql index 2e2c7b93..743468bd 100644 --- a/files/grest/rpc/account/account_assets.sql +++ b/files/grest/rpc/account/account_assets.sql @@ -34,7 +34,7 @@ BEGIN AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint WHERE sa.id = ANY(sa_id_list) - AND TX_IN.TX_IN_ID IS NULL + AND TX_IN.ID IS NULL GROUP BY sa.view, MA.policy, MA.name, MA.fingerprint ) diff --git a/files/grest/rpc/account/account_info.sql b/files/grest/rpc/account/account_info.sql index 4a64701f..b74512bf 100644 --- a/files/grest/rpc/account/account_info.sql +++ b/files/grest/rpc/account/account_info.sql @@ -119,7 +119,7 @@ BEGIN AND TX_OUT.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint WHERE TX_OUT.STAKE_ADDRESS_ID = ANY(sa_id_list) - AND TX_IN.TX_IN_ID IS NULL + AND TX_IN.ID IS NULL GROUP BY tx_out.stake_address_id ) UTXO_T ON UTXO_T.stake_address_id = status_t.id diff --git a/files/grest/rpc/address/address_assets.sql b/files/grest/rpc/address/address_assets.sql index f46c6548..9504d5bb 100644 --- a/files/grest/rpc/address/address_assets.sql +++ b/files/grest/rpc/address/address_assets.sql @@ -25,7 +25,7 @@ BEGIN AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint WHERE TXO.address = ANY(_addresses) - AND TX_IN.TX_IN_ID IS NULL + AND TX_IN.id IS NULL GROUP BY TXO.address, MA.policy, MA.name, ma.fingerprint ) diff --git a/files/grest/rpc/address/credential_utxos.sql b/files/grest/rpc/address/credential_utxos.sql new file mode 100644 index 00000000..16548ac9 --- /dev/null +++ b/files/grest/rpc/address/credential_utxos.sql @@ -0,0 +1,35 @@ +CREATE OR REPLACE FUNCTION grest.credential_utxos (_payment_credentials text[]) + RETURNS TABLE ( + tx_hash text, + index smallint, + balance text + ) + LANGUAGE PLPGSQL + AS $$ +DECLARE + _payment_cred_bytea bytea[]; + +BEGIN + SELECT INTO _payment_cred_bytea ARRAY_AGG(cred_bytea) + FROM ( + SELECT + DECODE(cred_hex, 'hex') AS cred_bytea + FROM + UNNEST(_payment_credentials) AS cred_hex + ) AS tmp; + + RETURN QUERY + SELECT + ENCODE(tx.hash, 'hex')::text as tx_hash, + tx_out.index::smallint, + tx_out.value::text AS balance + FROM tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id + AND tx_out.index = tx_in.tx_out_index + WHERE + payment_cred = any(_payment_cred_bytea) + AND + tx_in.id IS NULL; +END; +$$; diff --git a/files/grest/rpc/assets/asset_address_list.sql b/files/grest/rpc/assets/asset_address_list.sql deleted file mode 100644 index 6b02a1c9..00000000 --- a/files/grest/rpc/assets/asset_address_list.sql +++ /dev/null @@ -1,42 +0,0 @@ -CREATE FUNCTION grest.asset_address_list (_asset_policy text, _asset_name text default '') - RETURNS TABLE ( - payment_address varchar, - quantity text - ) LANGUAGE PLPGSQL - AS $$ -DECLARE - _asset_policy_decoded bytea; - _asset_name_decoded bytea; - _asset_id int; -BEGIN - SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - SELECT DECODE( - CASE WHEN _asset_name IS NULL - THEN '' - ELSE - _asset_name - END, - 'hex' - ) INTO _asset_name_decoded; - SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; - - RETURN QUERY - SELECT - TXO.ADDRESS, - SUM(MTX.QUANTITY)::text - FROM - MA_TX_OUT MTX - INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident - INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID - LEFT JOIN TX_IN ON TXO.TX_ID = TX_IN.TX_OUT_ID - AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint - WHERE - MA.id = _asset_id - AND TX_IN.TX_IN_ID IS NULL - GROUP BY - TXO.ADDRESS; -END; -$$; - -COMMENT ON FUNCTION grest.asset_address_list IS 'Get the list of all addresses containing a specific asset'; - diff --git a/files/grest/rpc/assets/asset_addresses.sql b/files/grest/rpc/assets/asset_addresses.sql new file mode 100644 index 00000000..74af6fe6 --- /dev/null +++ b/files/grest/rpc/assets/asset_addresses.sql @@ -0,0 +1,59 @@ +CREATE OR REPLACE FUNCTION grest.asset_address_list (_asset_policy text, _asset_name text default '') + RETURNS TABLE ( + payment_address varchar, + quantity text + ) LANGUAGE PLPGSQL + AS $$ +BEGIN + RETURN QUERY + SELECT * FROM grest.asset_addresses(_asset_policy, _asset_name); +END; +$$; + +CREATE OR REPLACE FUNCTION grest.asset_addresses (_asset_policy text, _asset_name text default '') + RETURNS TABLE ( + payment_address varchar, + quantity text + ) LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _asset_name_decoded bytea; + _asset_id int; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + SELECT DECODE( + CASE WHEN _asset_name IS NULL + THEN '' + ELSE + _asset_name + END, + 'hex' + ) INTO _asset_name_decoded; + SELECT id INTO _asset_id FROM multi_asset ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; + + RETURN QUERY + SELECT + x.address, + SUM(x.quantity)::text + FROM + ( + SELECT + txo.address, + mto.quantity + FROM + ma_tx_out mto + INNER JOIN tx_out txo ON txo.id = mto.tx_out_id + LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id + AND txo.index::smallint = tx_in.tx_out_index::smallint + WHERE + mto.ident = _asset_id + AND tx_in.id IS NULL + ) x + GROUP BY + x.address; +END; +$$; + +COMMENT ON FUNCTION grest.asset_address_list IS 'DEPRECATED!! Use asset_addresses instead.'; +COMMENT ON FUNCTION grest.asset_addresses IS 'Returns a list of addresses with quantity holding the specified asset'; diff --git a/files/grest/rpc/assets/asset_info_bulk.sql b/files/grest/rpc/assets/asset_info_bulk.sql index f34d68d0..1f6d7424 100644 --- a/files/grest/rpc/assets/asset_info_bulk.sql +++ b/files/grest/rpc/assets/asset_info_bulk.sql @@ -59,7 +59,7 @@ BEGIN FROM multi_asset ma INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id - LEFT JOIN tx ON tx.id = aic.last_mint_tx_id + INNER JOIN tx ON tx.id = aic.last_mint_tx_id LEFT JOIN grest.asset_registry_cache arc ON DECODE(arc.asset_policy, 'hex') = ma.policy AND DECODE(arc.asset_name, 'hex') = ma.name LEFT JOIN LATERAL ( SELECT diff --git a/files/grest/rpc/assets/asset_nft_address.sql b/files/grest/rpc/assets/asset_nft_address.sql new file mode 100644 index 00000000..b32f97b3 --- /dev/null +++ b/files/grest/rpc/assets/asset_nft_address.sql @@ -0,0 +1,40 @@ +CREATE OR REPLACE FUNCTION grest.asset_nft_address (_asset_policy text, _asset_name text default '') + RETURNS TABLE ( + payment_address varchar + ) LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; + _asset_name_decoded bytea; + _asset_id int; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + SELECT DECODE( + CASE WHEN _asset_name IS NULL + THEN '' + ELSE + _asset_name + END, + 'hex' + ) INTO _asset_name_decoded; + + SELECT id INTO _asset_id + FROM + multi_asset ma + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + WHERE + ma.policy = _asset_policy_decoded + AND ma.name = _asset_name_decoded + AND aic.total_supply = 1; + + RETURN QUERY + SELECT + address + FROM + tx_out + WHERE + id = (SELECT MAX(tx_out_id) FROM ma_tx_out WHERE ident = _asset_id); +END; +$$; + +COMMENT ON FUNCTION grest.asset_nft_address IS 'Returns the current address holding the specified NFT'; diff --git a/files/grest/rpc/assets/policy_asset_addresses.sql b/files/grest/rpc/assets/policy_asset_addresses.sql new file mode 100644 index 00000000..d0740cab --- /dev/null +++ b/files/grest/rpc/assets/policy_asset_addresses.sql @@ -0,0 +1,48 @@ +CREATE OR REPLACE FUNCTION grest.policy_asset_addresses (_asset_policy text) + RETURNS TABLE ( + asset_name text, + payment_address varchar, + quantity text + ) LANGUAGE PLPGSQL + AS $$ +DECLARE + _asset_policy_decoded bytea; +BEGIN + SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; + + RETURN QUERY + WITH + _all_assets AS ( + SELECT + id, + ENCODE(name, 'hex') AS asset_name + FROM + multi_asset ma + WHERE ma.policy = _asset_policy_decoded + ) + + SELECT + x.asset_name, + x.address, + SUM(x.quantity)::text + FROM + ( + SELECT + aa.asset_name, + txo.address, + mto.quantity + FROM + _all_assets aa + INNER JOIN ma_tx_out mto ON mto.ident = aa.id + INNER JOIN tx_out txo ON txo.id = mto.tx_out_id + LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id + AND txo.index::smallint = tx_in.tx_out_index::smallint + WHERE + tx_in.id IS NULL + ) x + GROUP BY + x.asset_name, x.address; +END; +$$; + +COMMENT ON FUNCTION grest.policy_asset_addresses IS 'Returns a list of addresses with quantity for each asset on a given policy'; diff --git a/files/grest/rpc/assets/asset_policy_info.sql b/files/grest/rpc/assets/policy_asset_info.sql similarity index 61% rename from files/grest/rpc/assets/asset_policy_info.sql rename to files/grest/rpc/assets/policy_asset_info.sql index fd3c8d79..8c4e6f2c 100644 --- a/files/grest/rpc/assets/asset_policy_info.sql +++ b/files/grest/rpc/assets/policy_asset_info.sql @@ -1,12 +1,36 @@ -CREATE FUNCTION grest.asset_policy_info (_asset_policy text) +CREATE OR REPLACE FUNCTION grest.asset_policy_info (_asset_policy text) RETURNS TABLE ( asset_name text, asset_name_ascii text, fingerprint varchar, + minting_tx_hash text, + total_supply text, + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, minting_tx_metadata jsonb, - token_registry_metadata jsonb, + token_registry_metadata json + ) + LANGUAGE PLPGSQL + AS $$ +BEGIN + RETURN QUERY + SELECT * FROM grest.policy_asset_info(_asset_policy); +END; +$$; + +CREATE OR REPLACE FUNCTION grest.policy_asset_info (_asset_policy text) + RETURNS TABLE ( + asset_name text, + asset_name_ascii text, + fingerprint varchar, + minting_tx_hash text, total_supply text, - creation_time integer + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, + minting_tx_metadata jsonb, + token_registry_metadata json ) LANGUAGE PLPGSQL AS $$ @@ -21,6 +45,11 @@ BEGIN ENCODE(ma.name, 'hex') AS asset_name, ENCODE(ma.name, 'escape') AS asset_name_ascii, ma.fingerprint, + ENCODE(tx.hash, 'hex'), + aic.total_supply::text, + aic.mint_cnt, + aic.burn_cnt, + EXTRACT(epoch FROM aic.creation_time)::integer, metadata.minting_tx_metadata, CASE WHEN arc.name IS NULL THEN NULL ELSE @@ -32,13 +61,11 @@ BEGIN 'logo', arc.logo, 'decimals', arc.decimals ) - END, - aic.total_supply, - EXTRACT(epoch from aic.creation_time)::integer + END FROM multi_asset ma INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id - LEFT JOIN tx ON tx.id = aic.last_mint_tx_id + INNER JOIN tx ON tx.id = aic.last_mint_tx_id LEFT JOIN grest.asset_registry_cache arc ON DECODE(arc.asset_policy, 'hex') = ma.policy AND DECODE(arc.asset_name, 'hex') = ma.name LEFT JOIN LATERAL ( SELECT diff --git a/files/grest/rpc/assets/asset_policy_list.sql b/files/grest/rpc/assets/policy_asset_list.sql similarity index 86% rename from files/grest/rpc/assets/asset_policy_list.sql rename to files/grest/rpc/assets/policy_asset_list.sql index b03ad416..b828be04 100644 --- a/files/grest/rpc/assets/asset_policy_list.sql +++ b/files/grest/rpc/assets/policy_asset_list.sql @@ -1,4 +1,4 @@ -CREATE FUNCTION grest.asset_policy_info (_asset_policy text) +CREATE OR REPLACE FUNCTION grest.policy_asset_list (_asset_policy text) RETURNS TABLE ( asset_name text, fingerprint varchar, @@ -18,7 +18,7 @@ BEGIN SELECT ENCODE(ma.name, 'hex') AS asset_name, ma.fingerprint AS fingerprint, - aic.total_supply, + aic.total_supply::text, aic.decimals FROM multi_asset ma diff --git a/files/grest/rpc/epoch/epoch_info.sql b/files/grest/rpc/epoch/epoch_info.sql index 8242aaff..8b0fd95b 100644 --- a/files/grest/rpc/epoch/epoch_info.sql +++ b/files/grest/rpc/epoch/epoch_info.sql @@ -46,7 +46,11 @@ BEGIN grest.epoch_info_cache ei LEFT JOIN grest.EPOCH_ACTIVE_STAKE_CACHE eas ON eas.epoch_no = ei.epoch_no WHERE - ei.epoch_no::text LIKE CASE WHEN _epoch_no IS NULL THEN '%' ELSE _epoch_no::text END + CASE WHEN _epoch_no IS NULL THEN + ei.epoch_no <= (SELECT MAX(epoch.no) FROM public.epoch) + ELSE + ei.epoch_no = _epoch_no + END AND (_include_next_epoch OR ei.i_first_block_time::integer is not null); END; diff --git a/files/grest/rpc/epoch/epoch_params.sql b/files/grest/rpc/epoch/epoch_params.sql index 5d8ad31f..951789c1 100644 --- a/files/grest/rpc/epoch/epoch_params.sql +++ b/files/grest/rpc/epoch/epoch_params.sql @@ -72,6 +72,8 @@ BEGIN ei.p_coins_per_utxo_size::text AS coins_per_utxo_size FROM grest.epoch_info_cache ei + WHERE + ei.epoch_no <= (SELECT MAX(epoch.no) FROM public.epoch) ORDER BY ei.epoch_no DESC; ELSE diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 5f9310bf..cdbaa80b 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -718,7 +718,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_first_only_and_empty" responses: "200": description: Array of payment addresses @@ -814,7 +814,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Token Registry description: Get a list of assets registered via token registry on github - /asset_address_list: #RPC + /asset_addresses: #RPC get: tags: - Asset @@ -827,7 +827,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_address_list" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": @@ -835,7 +835,52 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Asset Address List - description: Get the list of all addresses holding a given asset + description: Get the list of all addresses holding a given asset \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /asset_address_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of payment addresses holding the given token (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the list of all addresses holding a given asset (replaced by asset_addresses) + /asset_nft_address: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" + responses: + "200": + description: Payment addresses currently holding the given NFT + content: + application/json: + schema: + $ref: "#/components/schemas/asset_nft_address" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: NFT Address + description: Get the address where specified NFT currently reside on. /asset_info: #RPC get: tags: @@ -900,7 +945,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset History description: Get the mint/burn history of an asset - /asset_policy_info: #RPC + /policy_asset_addresses: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of asset names and payment addresses for the given policy (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset Address List + description: Get the list of addresses with quantity for each asset on the given policy \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /policy_asset_info: #RPC get: tags: - Asset @@ -912,15 +979,57 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_policy_info" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information + summary: Policy Asset Information description: Get the information for all assets under the same policy + /asset_policy_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the information for all assets under the same policy (replaced by asset_addresses) + /policy_asset_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) /asset_summary: #RPC get: tags: @@ -1410,7 +1519,27 @@ components: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: "41484c636f696e" + example: 41484c636f696e + in: query + required: false + allowEmptyValue: true + _asset_policy_nft: + deprecated: false + name: _asset_policy + description: NFT Policy ID in hexadecimal format (hex) + schema: + type: string + example: 187abba169e246fdcbebcd12915a8610cb470fda3b3fc21da0c4aed4 + in: query + required: true + allowEmptyValue: false + _asset_name_nft: + deprecated: false + name: _asset_name + description: NFT Name in hexadecimal format (hex) + schema: + type: string + example: 4f504b4559 in: query required: false allowEmptyValue: true @@ -1464,6 +1593,26 @@ components: in: query required: true allowEmptyValue: false + _first_only: + deprecated: false + name: _first_only + description: Returns the first entry only (overrides _empty to true) + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true + _empty: + deprecated: false + name: _empty + description: Include zero quantity entries + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true requestBodies: block_hashes: content: @@ -1547,6 +1696,32 @@ components: - stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2 - stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd _epoch_no: 1500 + stake_addresses_with_first_only_and_empty: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _first_only: + format: boolean + type: boolean + description: Only return the first result + _empty: + format: boolean + type: boolean + description: Include zero quantity entries + example: + _stake_addresses: + - stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2 + - stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd stake_addresses: content: application/json: @@ -2510,7 +2685,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2565,7 +2740,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_list: $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: @@ -2682,7 +2857,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" account_assets: type: array items: @@ -3130,7 +3305,7 @@ components: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" logo: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" - asset_address_list: + asset_addresses: type: array description: An array of payment addresses holding the given token (including balances) items: @@ -3143,6 +3318,8 @@ components: type: string description: Asset balance on the payment address example: 23 + asset_nft_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_summary: type: array items: @@ -3190,6 +3367,9 @@ components: type: string description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 + total_supply: + type: string + example: "35000" mint_cnt: type: integer description: Count of total mint transactions @@ -3198,6 +3378,10 @@ components: type: integer description: Count of total burn transactions example: 5 + creation_time: + type: integer + description: UNIX timestamp of the first asset mint + example: 1506635091 minting_tx_metadata: allOf: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -3226,13 +3410,6 @@ components: decimals: type: integer example: 0 - total_supply: - type: string - example: "35000" - creation_time: - type: integer - description: UNIX timestamp of the first asset mint - example: 1506635091 asset_history: type: array items: @@ -3265,7 +3442,18 @@ components: description: Array of Transaction Metadata for given transaction items: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" - asset_policy_info: + policy_asset_addresses: + type: array + description: Array of asset names and payment addresses for the given policy (including balances) + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + payment_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" + policy_asset_info: type: array description: List of policy assets items: @@ -3276,17 +3464,19 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_tx_hash: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_hash" + mint_cnt: + $ref: "#/components/schemas/asset_info/items/properties/mint_cnt" + burn_cnt: + $ref: "#/components/schemas/asset_info/items/properties/burn_cnt" + creation_time: + $ref: "#/components/schemas/asset_info/items/properties/creation_time" minting_tx_metadata: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" - total_supply: - type: string - example: "35000" - creation_time: - type: string - $ref: "#/components/schemas/asset_info/items/properties/creation_time" - asset_policy_list: + policy_asset_list: type: array description: List of policy assets items: diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 7128c9b6..ac59af5c 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -718,7 +718,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_first_only_and_empty" responses: "200": description: Array of payment addresses @@ -814,7 +814,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Token Registry description: Get a list of assets registered via token registry on github - /asset_address_list: #RPC + /asset_addresses: #RPC get: tags: - Asset @@ -827,7 +827,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_address_list" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": @@ -835,7 +835,52 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Asset Address List - description: Get the list of all addresses holding a given asset + description: Get the list of all addresses holding a given asset \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /asset_address_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of payment addresses holding the given token (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the list of all addresses holding a given asset (replaced by asset_addresses) + /asset_nft_address: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" + responses: + "200": + description: Payment addresses currently holding the given NFT + content: + application/json: + schema: + $ref: "#/components/schemas/asset_nft_address" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: NFT Address + description: Get the address where specified NFT currently reside on. /asset_info: #RPC get: tags: @@ -900,7 +945,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset History description: Get the mint/burn history of an asset - /asset_policy_info: #RPC + /policy_asset_addresses: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of asset names and payment addresses for the given policy (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset Address List + description: Get the list of addresses with quantity for each asset on the given policy \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /policy_asset_info: #RPC get: tags: - Asset @@ -912,15 +979,57 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_policy_info" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information + summary: Policy Asset Information description: Get the information for all assets under the same policy + /asset_policy_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the information for all assets under the same policy (replaced by asset_addresses) + /policy_asset_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) /asset_summary: #RPC get: tags: @@ -1410,7 +1519,27 @@ components: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: "424f4f4b" + example: 424f4f4b + in: query + required: false + allowEmptyValue: true + _asset_policy_nft: + deprecated: false + name: _asset_policy + description: NFT Policy ID in hexadecimal format (hex) + schema: + type: string + example: f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a + in: query + required: true + allowEmptyValue: false + _asset_name_nft: + deprecated: false + name: _asset_name + description: NFT Name in hexadecimal format (hex) + schema: + type: string + example: 68616e646c65 in: query required: false allowEmptyValue: true @@ -1464,6 +1593,26 @@ components: in: query required: true allowEmptyValue: false + _first_only: + deprecated: false + name: _first_only + description: Returns the first entry only (overrides _empty to true) + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true + _empty: + deprecated: false + name: _empty + description: Include zero quantity entries + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true requestBodies: block_hashes: content: @@ -1547,6 +1696,32 @@ components: - stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250 - stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy _epoch_no: 350 + stake_addresses_with_first_only_and_empty: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _first_only: + format: boolean + type: boolean + description: Only return the first result + _empty: + format: boolean + type: boolean + description: Include zero quantity entries + example: + _stake_addresses: + - stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250 + - stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy stake_addresses: content: application/json: @@ -2510,7 +2685,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2565,7 +2740,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_list: $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: @@ -2682,7 +2857,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" account_assets: type: array items: @@ -3130,7 +3305,7 @@ components: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" logo: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" - asset_address_list: + asset_addresses: type: array description: An array of payment addresses holding the given token (including balances) items: @@ -3143,6 +3318,8 @@ components: type: string description: Asset balance on the payment address example: 23 + asset_nft_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_summary: type: array items: @@ -3190,6 +3367,9 @@ components: type: string description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 + total_supply: + type: string + example: "35000" mint_cnt: type: integer description: Count of total mint transactions @@ -3198,6 +3378,10 @@ components: type: integer description: Count of total burn transactions example: 5 + creation_time: + type: integer + description: UNIX timestamp of the first asset mint + example: 1506635091 minting_tx_metadata: allOf: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -3226,13 +3410,6 @@ components: decimals: type: integer example: 0 - total_supply: - type: string - example: "35000" - creation_time: - type: integer - description: UNIX timestamp of the first asset mint - example: 1506635091 asset_history: type: array items: @@ -3265,7 +3442,18 @@ components: description: Array of Transaction Metadata for given transaction items: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" - asset_policy_info: + policy_asset_addresses: + type: array + description: Array of asset names and payment addresses for the given policy (including balances) + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + payment_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" + policy_asset_info: type: array description: List of policy assets items: @@ -3276,17 +3464,19 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_tx_hash: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_hash" + mint_cnt: + $ref: "#/components/schemas/asset_info/items/properties/mint_cnt" + burn_cnt: + $ref: "#/components/schemas/asset_info/items/properties/burn_cnt" + creation_time: + $ref: "#/components/schemas/asset_info/items/properties/creation_time" minting_tx_metadata: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" - total_supply: - type: string - example: "35000" - creation_time: - type: string - $ref: "#/components/schemas/asset_info/items/properties/creation_time" - asset_policy_list: + policy_asset_list: type: array description: List of policy assets items: diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 2f78bc43..41d0fd6c 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -718,7 +718,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_first_only_and_empty" responses: "200": description: Array of payment addresses @@ -814,7 +814,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Token Registry description: Get a list of assets registered via token registry on github - /asset_address_list: #RPC + /asset_addresses: #RPC get: tags: - Asset @@ -827,7 +827,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_address_list" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": @@ -835,7 +835,52 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Asset Address List - description: Get the list of all addresses holding a given asset + description: Get the list of all addresses holding a given asset \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /asset_address_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of payment addresses holding the given token (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the list of all addresses holding a given asset (replaced by asset_addresses) + /asset_nft_address: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" + responses: + "200": + description: Payment addresses currently holding the given NFT + content: + application/json: + schema: + $ref: "#/components/schemas/asset_nft_address" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: NFT Address + description: Get the address where specified NFT currently reside on. /asset_info: #RPC get: tags: @@ -900,7 +945,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset History description: Get the mint/burn history of an asset - /asset_policy_info: #RPC + /policy_asset_addresses: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of asset names and payment addresses for the given policy (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset Address List + description: Get the list of addresses with quantity for each asset on the given policy \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /policy_asset_info: #RPC get: tags: - Asset @@ -912,15 +979,57 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_policy_info" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information + summary: Policy Asset Information description: Get the information for all assets under the same policy + /asset_policy_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the information for all assets under the same policy (replaced by asset_addresses) + /policy_asset_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) /asset_summary: #RPC get: tags: @@ -1410,7 +1519,27 @@ components: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: "7447454e53" + example: 7447454e53 + in: query + required: false + allowEmptyValue: true + _asset_policy_nft: + deprecated: false + name: _asset_policy + description: NFT Policy ID in hexadecimal format (hex) + schema: + type: string + example: 002126e5e7cb2f5b6ac52ef2cdb9308ff58bf6e3b62e29df447cec72 + in: query + required: true + allowEmptyValue: false + _asset_name_nft: + deprecated: false + name: _asset_name + description: NFT Name in hexadecimal format (hex) + schema: + type: string + example: 74657374 in: query required: false allowEmptyValue: true @@ -1464,6 +1593,26 @@ components: in: query required: true allowEmptyValue: false + _first_only: + deprecated: false + name: _first_only + description: Returns the first entry only (overrides _empty to true) + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true + _empty: + deprecated: false + name: _empty + description: Include zero quantity entries + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true requestBodies: block_hashes: content: @@ -1547,6 +1696,32 @@ components: - stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l - stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx _epoch_no: 30 + stake_addresses_with_first_only_and_empty: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _first_only: + format: boolean + type: boolean + description: Only return the first result + _empty: + format: boolean + type: boolean + description: Include zero quantity entries + example: + _stake_addresses: + - stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l + - stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx stake_addresses: content: application/json: @@ -2510,7 +2685,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2565,7 +2740,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_list: $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: @@ -2682,7 +2857,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" account_assets: type: array items: @@ -3130,7 +3305,7 @@ components: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" logo: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" - asset_address_list: + asset_addresses: type: array description: An array of payment addresses holding the given token (including balances) items: @@ -3143,6 +3318,8 @@ components: type: string description: Asset balance on the payment address example: 23 + asset_nft_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_summary: type: array items: @@ -3190,6 +3367,9 @@ components: type: string description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 + total_supply: + type: string + example: "35000" mint_cnt: type: integer description: Count of total mint transactions @@ -3198,6 +3378,10 @@ components: type: integer description: Count of total burn transactions example: 5 + creation_time: + type: integer + description: UNIX timestamp of the first asset mint + example: 1506635091 minting_tx_metadata: allOf: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -3226,13 +3410,6 @@ components: decimals: type: integer example: 0 - total_supply: - type: string - example: "35000" - creation_time: - type: integer - description: UNIX timestamp of the first asset mint - example: 1506635091 asset_history: type: array items: @@ -3265,7 +3442,18 @@ components: description: Array of Transaction Metadata for given transaction items: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" - asset_policy_info: + policy_asset_addresses: + type: array + description: Array of asset names and payment addresses for the given policy (including balances) + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + payment_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" + policy_asset_info: type: array description: List of policy assets items: @@ -3276,17 +3464,19 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_tx_hash: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_hash" + mint_cnt: + $ref: "#/components/schemas/asset_info/items/properties/mint_cnt" + burn_cnt: + $ref: "#/components/schemas/asset_info/items/properties/burn_cnt" + creation_time: + $ref: "#/components/schemas/asset_info/items/properties/creation_time" minting_tx_metadata: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" - total_supply: - type: string - example: "35000" - creation_time: - type: string - $ref: "#/components/schemas/asset_info/items/properties/creation_time" - asset_policy_list: + policy_asset_list: type: array description: List of policy assets items: diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 1484a329..f05b8af7 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -718,7 +718,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_first_only_and_empty" responses: "200": description: Array of payment addresses @@ -814,7 +814,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Token Registry description: Get a list of assets registered via token registry on github - /asset_address_list: #RPC + /asset_addresses: #RPC get: tags: - Asset @@ -827,7 +827,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_address_list" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": @@ -835,7 +835,52 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Asset Address List - description: Get the list of all addresses holding a given asset + description: Get the list of all addresses holding a given asset \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /asset_address_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of payment addresses holding the given token (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the list of all addresses holding a given asset (replaced by asset_addresses) + /asset_nft_address: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" + responses: + "200": + description: Payment addresses currently holding the given NFT + content: + application/json: + schema: + $ref: "#/components/schemas/asset_nft_address" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: NFT Address + description: Get the address where specified NFT currently reside on. /asset_info: #RPC get: tags: @@ -900,7 +945,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset History description: Get the mint/burn history of an asset - /asset_policy_info: #RPC + /policy_asset_addresses: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of asset names and payment addresses for the given policy (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset Address List + description: Get the list of addresses with quantity for each asset on the given policy \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /policy_asset_info: #RPC get: tags: - Asset @@ -912,15 +979,57 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_policy_info" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information + summary: Policy Asset Information description: Get the information for all assets under the same policy + /asset_policy_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the information for all assets under the same policy (replaced by asset_addresses) + /policy_asset_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) /asset_summary: #RPC get: tags: @@ -1410,7 +1519,27 @@ components: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: "433374" + example: 433374 + in: query + required: false + allowEmptyValue: true + _asset_policy_nft: + deprecated: false + name: _asset_policy + description: NFT Policy ID in hexadecimal format (hex) + schema: + type: string + example: 005b8ca355aec6125531ebea89bf9ef8df90121ea5717f0c55027e35 + in: query + required: true + allowEmptyValue: false + _asset_name_nft: + deprecated: false + name: _asset_name + description: NFT Name in hexadecimal format (hex) + schema: + type: string + example: 4d43 in: query required: false allowEmptyValue: true @@ -1464,6 +1593,26 @@ components: in: query required: true allowEmptyValue: false + _first_only: + deprecated: false + name: _first_only + description: Returns the first entry only (overrides _empty to true) + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true + _empty: + deprecated: false + name: _empty + description: Include zero quantity entries + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true requestBodies: block_hashes: content: @@ -1547,6 +1696,32 @@ components: - stake_test1upv7n2x0lxepkyx8ux2gjt74ecaa39tjgaccxl6hw5fwzngpzf5zt - stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl _epoch_no: 11 + stake_addresses_with_first_only_and_empty: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _first_only: + format: boolean + type: boolean + description: Only return the first result + _empty: + format: boolean + type: boolean + description: Include zero quantity entries + example: + _stake_addresses: + - stake_test1upv7n2x0lxepkyx8ux2gjt74ecaa39tjgaccxl6hw5fwzngpzf5zt + - stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl stake_addresses: content: application/json: @@ -2510,7 +2685,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2565,7 +2740,7 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_list: $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: @@ -2682,7 +2857,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" account_assets: type: array items: @@ -3130,7 +3305,7 @@ components: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" logo: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" - asset_address_list: + asset_addresses: type: array description: An array of payment addresses holding the given token (including balances) items: @@ -3143,6 +3318,8 @@ components: type: string description: Asset balance on the payment address example: 23 + asset_nft_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_summary: type: array items: @@ -3190,6 +3367,9 @@ components: type: string description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 + total_supply: + type: string + example: "35000" mint_cnt: type: integer description: Count of total mint transactions @@ -3198,6 +3378,10 @@ components: type: integer description: Count of total burn transactions example: 5 + creation_time: + type: integer + description: UNIX timestamp of the first asset mint + example: 1506635091 minting_tx_metadata: allOf: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -3226,13 +3410,6 @@ components: decimals: type: integer example: 0 - total_supply: - type: string - example: "35000" - creation_time: - type: integer - description: UNIX timestamp of the first asset mint - example: 1506635091 asset_history: type: array items: @@ -3265,7 +3442,18 @@ components: description: Array of Transaction Metadata for given transaction items: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" - asset_policy_info: + policy_asset_addresses: + type: array + description: Array of asset names and payment addresses for the given policy (including balances) + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + payment_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" + policy_asset_info: type: array description: List of policy assets items: @@ -3276,17 +3464,19 @@ components: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_tx_hash: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_hash" + mint_cnt: + $ref: "#/components/schemas/asset_info/items/properties/mint_cnt" + burn_cnt: + $ref: "#/components/schemas/asset_info/items/properties/burn_cnt" + creation_time: + $ref: "#/components/schemas/asset_info/items/properties/creation_time" minting_tx_metadata: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" - total_supply: - type: string - example: "35000" - creation_time: - type: string - $ref: "#/components/schemas/asset_info/items/properties/creation_time" - asset_policy_list: + policy_asset_list: type: array description: List of policy assets items: diff --git a/specs/templates/2-api-params.yaml b/specs/templates/2-api-params.yaml index 3cf58b97..e9610c4c 100644 --- a/specs/templates/2-api-params.yaml +++ b/specs/templates/2-api-params.yaml @@ -146,7 +146,27 @@ parameters: description: Asset Name in hexadecimal format (hex), empty asset name returns royalties schema: type: string - example: "##_asset_name_param##" + example: ##_asset_name_param## + in: query + required: false + allowEmptyValue: true + _asset_policy_nft: + deprecated: false + name: _asset_policy + description: NFT Policy ID in hexadecimal format (hex) + schema: + type: string + example: ##_asset_policy_nft_param## + in: query + required: true + allowEmptyValue: false + _asset_name_nft: + deprecated: false + name: _asset_name + description: NFT Name in hexadecimal format (hex) + schema: + type: string + example: ##_asset_name_nft_param## in: query required: false allowEmptyValue: true @@ -200,3 +220,23 @@ parameters: in: query required: true allowEmptyValue: false + _first_only: + deprecated: false + name: _first_only + description: Returns the first entry only (overrides _empty to true) + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true + _empty: + deprecated: false + name: _empty + description: Include zero quantity entries + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true diff --git a/specs/templates/3-api-requestBodies.yaml b/specs/templates/3-api-requestBodies.yaml index f0f25af0..639f9267 100644 --- a/specs/templates/3-api-requestBodies.yaml +++ b/specs/templates/3-api-requestBodies.yaml @@ -81,6 +81,32 @@ requestBodies: - ##stake_addresses1_rb## - ##stake_addresses2_rb## _epoch_no: ##epoch_no_rb## + stake_addresses_with_first_only_and_empty: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _first_only: + format: boolean + type: boolean + description: Only return the first result + _empty: + format: boolean + type: boolean + description: Include zero quantity entries + example: + _stake_addresses: + - ##stake_addresses1_rb## + - ##stake_addresses2_rb## stake_addresses: content: application/json: diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 9909537c..055d4327 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -822,7 +822,7 @@ schemas: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" balance: type: string description: Sum of all UTxO values beloning to address @@ -877,7 +877,7 @@ schemas: type: object properties: address: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_list: $ref: "#/components/schemas/account_assets/items/properties/asset_list" credential_txs: @@ -994,7 +994,7 @@ schemas: addresses: type: array items: - $ref: "#/components/schemas/asset_address_list/items/properties/payment_address" + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" account_assets: type: array items: @@ -1442,7 +1442,7 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" logo: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/logo" - asset_address_list: + asset_addresses: type: array description: An array of payment addresses holding the given token (including balances) items: @@ -1455,6 +1455,8 @@ schemas: type: string description: Asset balance on the payment address example: 23 + asset_nft_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" asset_summary: type: array items: @@ -1502,6 +1504,9 @@ schemas: type: string description: Hash of the latest mint transaction example: cb07b7e51b77079776c4a78f2daf8f14f9945d2b047da7bfcb71d7fbb9f86712 + total_supply: + type: string + example: "35000" mint_cnt: type: integer description: Count of total mint transactions @@ -1510,6 +1515,10 @@ schemas: type: integer description: Count of total burn transactions example: 5 + creation_time: + type: integer + description: UNIX timestamp of the first asset mint + example: 1506635091 minting_tx_metadata: allOf: - $ref: "#/components/schemas/tx_metadata/items/properties/metadata" @@ -1538,13 +1547,6 @@ schemas: decimals: type: integer example: 0 - total_supply: - type: string - example: "35000" - creation_time: - type: integer - description: UNIX timestamp of the first asset mint - example: 1506635091 asset_history: type: array items: @@ -1577,7 +1579,18 @@ schemas: description: Array of Transaction Metadata for given transaction items: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" - asset_policy_info: + policy_asset_addresses: + type: array + description: Array of asset names and payment addresses for the given policy (including balances) + items: + properties: + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + payment_address: + $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" + policy_asset_info: type: array description: List of policy assets items: @@ -1588,17 +1601,19 @@ schemas: $ref: "#/components/schemas/asset_info/items/properties/asset_name_ascii" fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + minting_tx_hash: + $ref: "#/components/schemas/asset_info/items/properties/minting_tx_hash" + mint_cnt: + $ref: "#/components/schemas/asset_info/items/properties/mint_cnt" + burn_cnt: + $ref: "#/components/schemas/asset_info/items/properties/burn_cnt" + creation_time: + $ref: "#/components/schemas/asset_info/items/properties/creation_time" minting_tx_metadata: $ref: "#/components/schemas/asset_info/items/properties/minting_tx_metadata" token_registry_metadata: $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata" - total_supply: - type: string - example: "35000" - creation_time: - type: string - $ref: "#/components/schemas/asset_info/items/properties/creation_time" - asset_policy_list: + policy_asset_list: type: array description: List of policy assets items: diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index 5d012f6f..b712d813 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -523,7 +523,7 @@ paths: tags: - Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_first_only_and_empty" responses: "200": description: Array of payment addresses @@ -619,7 +619,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset Token Registry description: Get a list of assets registered via token registry on github - /asset_address_list: #RPC + /asset_addresses: #RPC get: tags: - Asset @@ -632,7 +632,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_address_list" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": @@ -640,7 +640,52 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Asset Address List - description: Get the list of all addresses holding a given asset + description: Get the list of all addresses holding a given asset \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /asset_address_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + responses: + "200": + description: Array of payment addresses holding the given token (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the list of all addresses holding a given asset (replaced by asset_addresses) + /asset_nft_address: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" + responses: + "200": + description: Payment addresses currently holding the given NFT + content: + application/json: + schema: + $ref: "#/components/schemas/asset_nft_address" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: NFT Address + description: Get the address where specified NFT currently reside on. /asset_info: #RPC get: tags: @@ -705,7 +750,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset History description: Get the mint/burn history of an asset - /asset_policy_info: #RPC + /policy_asset_addresses: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of asset names and payment addresses for the given policy (including balances) + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_addresses" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset Address List + description: Get the list of addresses with quantity for each asset on the given policy \ + "Note: Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for few of the most active projects that have millions of transactions, this will likely end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to query layers to have a dedicated cache table for themselves served via Koios." + /policy_asset_info: #RPC get: tags: - Asset @@ -717,15 +784,57 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/asset_policy_info" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information + summary: Policy Asset Information description: Get the information for all assets under the same policy + /asset_policy_info: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: DEPRECATED + description: Get the information for all assets under the same policy (replaced by asset_addresses) + /policy_asset_list: #RPC + get: + tags: + - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" + responses: + "200": + description: Array of detailed information of assets under the same policy + content: + application/json: + schema: + $ref: "#/components/schemas/policy_asset_list" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) /asset_summary: #RPC get: tags: diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index 95ae68be..c4854648 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -54,6 +54,18 @@ "pv": "433374", "pp": "7447454e53" }, + "_asset_policy_nft": { + "m": "f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a", + "g": "187abba169e246fdcbebcd12915a8610cb470fda3b3fc21da0c4aed4", + "pv": "005b8ca355aec6125531ebea89bf9ef8df90121ea5717f0c55027e35", + "pp": "002126e5e7cb2f5b6ac52ef2cdb9308ff58bf6e3b62e29df447cec72" + }, + "_asset_name_nft": { + "m": "68616e646c65", + "g": "4f504b4559", + "pv": "4d43", + "pp": "74657374" + }, "_pool_bech32": { "m": "pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc", "g": "pool1xc9eywck4e20tydz4yvh5vfe0ep8whawvwz8wqkc9k046a2ypp4", From ea12a1b4b89e6d916f3aaa7dc4beed4244d54331 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 1 Feb 2023 13:14:37 +0100 Subject: [PATCH 38/86] Add files via upload (#161) --- images/Logo-black2.png | Bin 61512 -> 89791 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/Logo-black2.png b/images/Logo-black2.png index 7b50d3d387a84c1944c2104231bef1159df6a75a..bb6aa9bd6d04fe60fc5e0a1d523083e1c07b1ab8 100644 GIT binary patch literal 89791 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

t3~ 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

pm0xQl6 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

|iy9IX31&Nr`?^&Ip+?QdDOJ@X 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%0AmYYykVk0Ck7mW*1jZLA3&vF0X*Ezm_*Tw(YA+U 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%0A?f%D5$*>5Hx|=IUwG%DfaB`O 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 J!y~c}^Z!KxRto?C literal 61512 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

  • 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 c-g@{W!6@838|iZDXxc4V$q40Z(RL{{R30 From 8404351f82ba17e6620a946beb0be3fd82a2bf69 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 5 Feb 2023 22:09:08 +0100 Subject: [PATCH 39/86] Update projects.json (#164) added ETRNL logo --- projects.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects.json b/projects.json index dd01b20e..de6bdbee 100644 --- a/projects.json +++ b/projects.json @@ -67,6 +67,10 @@ { "text": "Eternl", "link": "https://eternl.io/" + "logo": { + "dark": "https://eternl.io/images/eternl-512.png", + "light": "https://eternl.io/images/eternl-512.png" + } }, { "text": "Mesh SDK", From d119f185db319bd6ab95918fee926ec1f85af07a Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 6 Feb 2023 23:26:08 +0100 Subject: [PATCH 40/86] Update projects.json (#165) --- projects.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects.json b/projects.json index de6bdbee..1e5f3244 100644 --- a/projects.json +++ b/projects.json @@ -66,7 +66,7 @@ }, { "text": "Eternl", - "link": "https://eternl.io/" + "link": "https://eternl.io/", "logo": { "dark": "https://eternl.io/images/eternl-512.png", "light": "https://eternl.io/images/eternl-512.png" From 79fb1f45b9a0fc47cfbed9eafc4f060f7dc38505 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 7 Feb 2023 14:16:44 +0100 Subject: [PATCH 41/86] Update projects.json (#166) --- projects.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects.json b/projects.json index 1e5f3244..6aac0876 100644 --- a/projects.json +++ b/projects.json @@ -76,8 +76,8 @@ "text": "Mesh SDK", "link": "https://meshjs.dev", "logo": { - "dark": "https://meshjs.dev/logo-mesh/black/logo-mesh-black-256x256.png", - "light": "https://meshjs.dev/logo-mesh/white/logo-mesh-white-256x256.png" + "dark": "https://meshjs.dev/logo-mesh/white/logo-mesh-white-256x256.png", + "light": "https://meshjs.dev/logo-mesh/black/logo-mesh-black-256x256.png" } }, { From 07bebcb53bb657f03db7f6563ec2de65045fb276 Mon Sep 17 00:00:00 2001 From: Quixote <82296005+QuixoteSystems@users.noreply.github.com> Date: Thu, 9 Feb 2023 02:12:07 +0100 Subject: [PATCH 42/86] Adding Raw Cardano Explorer Logo (#167) * Adding Raw Cardano Explorer Logo * Typo fix --------- Co-authored-by: RdLrT <3169068+rdlrt@users.noreply.github.com> --- projects.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/projects.json b/projects.json index 6aac0876..bdb8b26b 100644 --- a/projects.json +++ b/projects.json @@ -102,7 +102,11 @@ }, { "text": "Raw Cardano Explorer", - "link": "https://rawcardano.app" + "link": "https://rawcardano.app", + "logo": { + "dark": "https://www.quixote.systems/wp-content/uploads/2022/06/raw_explorer_logo.png", + "light": "https://www.quixote.systems/wp-content/uploads/2022/06/raw_explorer_logo.png" + } } ] } From 4764785c00b356e1590d9fd1bf48260f6e246b8e Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 9 Feb 2023 13:10:36 +0100 Subject: [PATCH 43/86] Add files via upload (#168) logo for the missing projects related images --- images/Koios-logo-dummy.png | Bin 0 -> 157592 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/Koios-logo-dummy.png diff --git a/images/Koios-logo-dummy.png b/images/Koios-logo-dummy.png new file mode 100644 index 0000000000000000000000000000000000000000..d5d736b1ed478d4e4b9d4ce59d58ef7c7776db1f GIT binary patch literal 157592 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 Date: Thu, 9 Feb 2023 13:10:50 +0100 Subject: [PATCH 44/86] Update projects.json (#169) fixed the Musicbox logo links, added dummy logos for the missing one. --- projects.json | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/projects.json b/projects.json index bdb8b26b..e4accdd1 100644 --- a/projects.json +++ b/projects.json @@ -38,19 +38,35 @@ "Community Projects/Tools": [ { "text": "ADABox", - "link": "https://explorer.adabox.io/pools" + "link": "https://explorer.adabox.io/pools", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png" + } }, { "text": "Building On Cardano", - "link": "https://buildingoncardano.com" + "link": "https://buildingoncardano.com", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png" + } }, { "text": "CardaStat", - "link": "https://cardastat.info" + "link": "https://cardastat.info", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png" + } }, { "text": "CNFT.IO", - "link": "https://CNFT.IO" + "link": "https://CNFT.IO", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png" + } }, { "text": "CNTools", @@ -62,7 +78,11 @@ }, { "text": "Dandelion", - "link": "https://dandelion.link" + "link": "https://dandelion.link", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png" + } }, { "text": "Eternl", @@ -84,13 +104,17 @@ "text": "MusicBox", "link": "https://musicboxnft.com", "logo": { - "dark": "https://github.com/adabox-aio/musicbox-website/blob/main/src/main/resources/static/favicon/android-icon-192x192.png", - "light": "https://github.com/adabox-aio/musicbox-website/blob/main/src/main/resources/static/favicon/android-icon-192x192.png" + "dark": "https://raw.githubusercontent.com/adabox-aio/musicbox-website/main/src/main/resources/static/favicon/android-icon-192x192.png", + "light": "https://raw.githubusercontent.com/adabox-aio/musicbox-website/main/src/main/resources/static/favicon/android-icon-192x192.png" } }, { "text": "Poolpeek", - "link": "https://poolpeek.com" + "link": "https://poolpeek.com", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/Koios-logo-dummy.png" + } }, { "text": "TosiDrop", From 52be67b2a3224e218c52f09940bd2d5ceeb6902f Mon Sep 17 00:00:00 2001 From: Abdelkrim Dib Date: Fri, 17 Feb 2023 10:06:33 +0100 Subject: [PATCH 45/86] Update Mesh SDK logo (#170) * Update Mesh SDK logo * Update projects.json Mesh logo updated --------- Co-authored-by: Michael --- projects.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects.json b/projects.json index e4accdd1..ea456df1 100644 --- a/projects.json +++ b/projects.json @@ -96,8 +96,8 @@ "text": "Mesh SDK", "link": "https://meshjs.dev", "logo": { - "dark": "https://meshjs.dev/logo-mesh/white/logo-mesh-white-256x256.png", - "light": "https://meshjs.dev/logo-mesh/black/logo-mesh-black-256x256.png" + "dark": "https://meshjs.dev/logo-mesh/white/logo-mesh-white-300x300.png", + "light": "https://meshjs.dev/logo-mesh/black/logo-mesh-black-300x300.png" } }, { From 50993af609c3730366fcc2fae1ef93d8e0300948 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 22 Feb 2023 21:58:11 +0000 Subject: [PATCH 46/86] Update projects.json (#171) added gLiveview --- projects.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects.json b/projects.json index ea456df1..0d59cb3d 100644 --- a/projects.json +++ b/projects.json @@ -69,7 +69,7 @@ } }, { - "text": "CNTools", + "text": "CNTools/gLiveview", "link": "https://cardano-community.github.io/guild-operators/Scripts/cntools/", "logo": { "dark": "https://github.com/cardano-community/guild-operators/raw/alpha/guild.png", From b3b91b59c7b7a63d80b48f532f4c49ccdf3e47d8 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Thu, 23 Feb 2023 23:54:43 +1100 Subject: [PATCH 47/86] Koios-1.0.10rc (#159) ## Description (for this PR) - [x] Update version to Koios-1.0.10rc - [x] Add account_utxos & specs update, closes #146 - [x] Fix pool_delegators endpoint specs doc, closes #157 - [x] Add param_updates, closes #158 - [x] Update changelog - [x] Make input [hex] params case insensitive for asset endpoints - [x] Uncertainty around adopting dbsync 13.1.0.0 (would require manually creating indexes for those missing) - [x] Investigate `tx_info` performance issue for 13.1.0.0 (some addresses only) - [x] Include missing indexes re-creation as part of setup-grest on guild-ops repo - [x] Update docs for configuring statement_timeout in postgrest - [x] Test & Resolve any issues reported via schemathesis - [x] Prepare Draft Release notes - [x] Mention in notes asking to shutdown dbsync if up during upgrade to 13.1.0.0 - as it can cause locks --- .../grest/rpc/00_blockchain/param_updates.sql | 59 ++++++++ .../01_cached_tables/asset_registry_cache.sql | 4 +- files/grest/rpc/02_indexes/13_1_00.sql | 26 ++++ files/grest/rpc/account/account_addresses.sql | 2 +- files/grest/rpc/account/account_assets.sql | 6 +- files/grest/rpc/account/account_info.sql | 2 +- files/grest/rpc/account/account_utxos.sql | 36 +++++ files/grest/rpc/address/address_assets.sql | 4 +- files/grest/rpc/address/address_info.sql | 2 +- files/grest/rpc/address/credential_utxos.sql | 2 +- files/grest/rpc/assets/asset_addresses.sql | 2 +- files/grest/rpc/assets/asset_info.sql | 60 +++++++- files/grest/rpc/assets/asset_info_bulk.sql | 2 +- .../rpc/assets/policy_asset_addresses.sql | 2 +- files/grest/rpc/assets/policy_asset_info.sql | 2 +- files/grest/rpc/transactions/tx_info.sql | 93 +++++++----- .../grest/rpc/views/asset_token_registry.sql | 2 + html/index.html | 4 +- specs/results/koiosapi-guild.yaml | 142 +++++++++++++----- specs/results/koiosapi-mainnet.yaml | 138 +++++++++++++---- specs/results/koiosapi-preprod.yaml | 138 +++++++++++++---- specs/results/koiosapi-preview.yaml | 138 +++++++++++++---- specs/templates/2-api-params.yaml | 2 +- specs/templates/4-api-schemas.yaml | 50 ++++-- specs/templates/api-main.yaml | 86 ++++++++--- specs/templates/example-map.json | 4 +- 26 files changed, 787 insertions(+), 221 deletions(-) create mode 100644 files/grest/rpc/00_blockchain/param_updates.sql create mode 100644 files/grest/rpc/02_indexes/13_1_00.sql create mode 100644 files/grest/rpc/account/account_utxos.sql diff --git a/files/grest/rpc/00_blockchain/param_updates.sql b/files/grest/rpc/00_blockchain/param_updates.sql new file mode 100644 index 00000000..cf6f5595 --- /dev/null +++ b/files/grest/rpc/00_blockchain/param_updates.sql @@ -0,0 +1,59 @@ +CREATE OR REPLACE FUNCTION grest.param_updates () + RETURNS TABLE ( + tx_hash text, + block_height word31type, + block_time integer, + epoch_no word31type, + data jsonb + ) + LANGUAGE PLPGSQL + AS $$ + +BEGIN + RETURN QUERY + SELECT DISTINCT ON (pp.registered_tx_id) + ENCODE(t.hash,'hex') as tx_hash, + b.block_no AS block_height, + EXTRACT(epoch from b.time)::integer as block_time, + b.epoch_no, + JSONB_STRIP_NULLS(JSONB_BUILD_OBJECT( + 'min_fee_a', pp.min_fee_a, + 'min_fee_b', pp.min_fee_b, + 'max_block_size', pp.max_block_size, + 'max_tx_size', pp.max_tx_size, + 'max_bh_size', pp.max_bh_size, + 'key_deposit', pp.key_deposit, + 'pool_deposit', pp.pool_deposit, + 'max_epoch', pp.max_epoch, + 'optimal_pool_count', pp.optimal_pool_count, + 'influence', pp.influence, + 'monetary_expand_rate', pp.monetary_expand_rate, + 'treasury_growth_rate', pp.treasury_growth_rate, + 'decentralisation', pp.decentralisation, + 'entropy', pp.entropy, + 'protocol_major', pp.protocol_major, + 'protocol_minor', pp.protocol_minor, + 'min_utxo_value', pp.min_utxo_value, + 'min_pool_cost', pp.min_pool_cost, + 'cost_model', CM.costs, + 'price_mem', pp.price_mem, + 'price_step', pp.price_step, + 'max_tx_ex_mem', pp.max_tx_ex_mem, + 'max_tx_ex_steps', pp.max_tx_ex_steps, + 'max_block_ex_mem', pp.max_block_ex_mem, + 'max_block_ex_steps', pp.max_block_ex_steps, + 'max_val_size', pp.max_val_size, + 'collateral_percent', pp.collateral_percent, + 'max_collateral_inputs', pp.max_collateral_inputs, + 'coins_per_utxo_size', pp.coins_per_utxo_size + )) AS data + FROM + public.param_proposal pp + INNER JOIN tx t ON t.id = pp.registered_tx_id + INNER JOIN block b ON t.block_id = b.id + LEFT JOIN cost_model CM ON CM.id = pp.cost_model_id + ; +END; +$$; + +COMMENT ON FUNCTION grest.param_updates IS 'Parameter updates applied to the network'; diff --git a/files/grest/rpc/01_cached_tables/asset_registry_cache.sql b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql index e54d23d4..407563f2 100644 --- a/files/grest/rpc/01_cached_tables/asset_registry_cache.sql +++ b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql @@ -1,4 +1,4 @@ -DROP TABLE IF EXISTS grest.asset_registry_cache; +DROP TABLE IF EXISTS grest.asset_registry_cache CASCADE; CREATE TABLE grest.asset_registry_cache ( asset_policy text NOT NULL, @@ -56,4 +56,4 @@ BEGIN logo = _logo, decimals = _decimals; END; -$$; \ No newline at end of file +$$; diff --git a/files/grest/rpc/02_indexes/13_1_00.sql b/files/grest/rpc/02_indexes/13_1_00.sql new file mode 100644 index 00000000..4c18b47b --- /dev/null +++ b/files/grest/rpc/02_indexes/13_1_00.sql @@ -0,0 +1,26 @@ +/* Unique Indexes that got dropped at 13.1.0.0 */ +CREATE UNIQUE INDEX IF NOT EXISTS unique_ada_pots ON public.ada_pots USING btree (block_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_col_txin ON public.collateral_tx_in USING btree (tx_in_id, tx_out_id, tx_out_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_col_txout ON public.collateral_tx_out USING btree (tx_id, index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_delegation ON public.delegation USING btree (tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_epoch_param ON public.epoch_param USING btree (epoch_no, block_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_ma_tx_mint ON public.ma_tx_mint USING btree (ident, tx_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_ma_tx_out ON public.ma_tx_out USING btree (ident, tx_out_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_param_proposal ON public.param_proposal USING btree (key, registered_tx_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_pool_owner ON public.pool_owner USING btree (addr_id, pool_update_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_pool_relay ON public.pool_relay USING btree (update_id, ipv4, ipv6, dns_name); +CREATE UNIQUE INDEX IF NOT EXISTS unique_pool_retiring ON public.pool_retire USING btree (announced_tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_pool_update ON public.pool_update USING btree (registered_tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_pot_transfer ON public.pot_transfer USING btree (tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_redeemer ON public.redeemer USING btree (tx_id, purpose, index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_ref_tx_in ON reference_tx_in USING btree (tx_in_id, tx_out_id, tx_out_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_reserves ON public.reserve USING btree (addr_id, tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_stake_deregistration ON public.stake_deregistration USING btree (tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_stake_registration ON public.stake_registration USING btree (tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_treasury ON public.treasury USING btree (addr_id, tx_id, cert_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_tx_metadata ON public.tx_metadata USING btree (key, tx_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_txin ON tx_in USING btree (tx_out_id, tx_out_index); +CREATE UNIQUE INDEX IF NOT EXISTS unique_withdrawal ON public.withdrawal USING btree (addr_id, tx_id); + +/* Help multi asset queries */ +CREATE INDEX IF NOT EXISTS idx_ma_tx_out_ident ON ma_tx_out (ident) ; diff --git a/files/grest/rpc/account/account_addresses.sql b/files/grest/rpc/account/account_addresses.sql index b46e04ce..7170fd54 100644 --- a/files/grest/rpc/account/account_addresses.sql +++ b/files/grest/rpc/account/account_addresses.sql @@ -33,7 +33,7 @@ BEGIN AND txo.index::smallint = tx_in.tx_out_index::smallint WHERE txo.stake_address_id = ANY(sa_id_list) - AND tx_in.id IS NULL + AND TX_IN.TX_OUT_ID IS NULL ) x ) SELECT diff --git a/files/grest/rpc/account/account_assets.sql b/files/grest/rpc/account/account_assets.sql index 743468bd..e3098c91 100644 --- a/files/grest/rpc/account/account_assets.sql +++ b/files/grest/rpc/account/account_assets.sql @@ -1,4 +1,4 @@ -CREATE FUNCTION grest.account_assets (_stake_addresses text[]) +CREATE OR REPLACE FUNCTION grest.account_assets (_stake_addresses text[]) RETURNS TABLE ( stake_address varchar, asset_list json @@ -34,9 +34,9 @@ BEGIN AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint WHERE sa.id = ANY(sa_id_list) - AND TX_IN.ID IS NULL + AND TX_IN.TX_OUT_ID IS NULL GROUP BY - sa.view, MA.policy, MA.name, MA.fingerprint + sa.view, MA.policy, MA.name, MA.fingerprint, aic.decimals ) SELECT diff --git a/files/grest/rpc/account/account_info.sql b/files/grest/rpc/account/account_info.sql index b74512bf..cde440cf 100644 --- a/files/grest/rpc/account/account_info.sql +++ b/files/grest/rpc/account/account_info.sql @@ -119,7 +119,7 @@ BEGIN AND TX_OUT.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint WHERE TX_OUT.STAKE_ADDRESS_ID = ANY(sa_id_list) - AND TX_IN.ID IS NULL + AND TX_IN.TX_OUT_ID IS NULL GROUP BY tx_out.stake_address_id ) UTXO_T ON UTXO_T.stake_address_id = status_t.id diff --git a/files/grest/rpc/account/account_utxos.sql b/files/grest/rpc/account/account_utxos.sql new file mode 100644 index 00000000..5c0a0027 --- /dev/null +++ b/files/grest/rpc/account/account_utxos.sql @@ -0,0 +1,36 @@ +CREATE OR REPLACE FUNCTION grest.account_utxos (_stake_address text) + RETURNS TABLE ( + tx_hash text, + tx_index smallint, + address varchar, + value text, + block_height word31type, + block_time integer + ) + LANGUAGE PLPGSQL + AS $$ + +BEGIN + RETURN QUERY + SELECT + ENCODE(tx.hash,'hex') as tx_hash, + tx_out.index::smallint as tx_index, + tx_out.address, + tx_out.value::text as value, + b.block_no as block_height, + EXTRACT(epoch from b.time)::integer as block_time + FROM + tx_out + LEFT JOIN tx_in ON tx_in.tx_out_id = tx_out.tx_id + AND tx_in.tx_out_index = tx_out.index + INNER JOIN tx ON tx.id = tx_out.tx_id + LEFT JOIN block b ON b.id = tx.block_id + WHERE + tx_in.tx_out_id IS NULL + AND + tx_out.stake_address_id = (select id from stake_address where view = _stake_address); + +END; +$$; + +COMMENT ON FUNCTION grest.account_utxos IS 'Get non-empty UTxOs associated with a given stake address'; diff --git a/files/grest/rpc/address/address_assets.sql b/files/grest/rpc/address/address_assets.sql index 9504d5bb..2e27c9e7 100644 --- a/files/grest/rpc/address/address_assets.sql +++ b/files/grest/rpc/address/address_assets.sql @@ -25,9 +25,9 @@ BEGIN AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint WHERE TXO.address = ANY(_addresses) - AND TX_IN.id IS NULL + AND TX_IN.tx_out_id IS NULL GROUP BY - TXO.address, MA.policy, MA.name, ma.fingerprint + TXO.address, MA.policy, MA.name, ma.fingerprint, aic.decimals ) SELECT diff --git a/files/grest/rpc/address/address_info.sql b/files/grest/rpc/address/address_info.sql index 3f0e43c4..b883d61e 100644 --- a/files/grest/rpc/address/address_info.sql +++ b/files/grest/rpc/address/address_info.sql @@ -43,7 +43,7 @@ BEGIN AND tx_in.tx_out_index = tx_out.index INNER JOIN tx ON tx.id = tx_out.tx_id WHERE - tx_in.id IS NULL + tx_in.tx_out_id IS NULL AND tx_out.address = ANY(_addresses) ) diff --git a/files/grest/rpc/address/credential_utxos.sql b/files/grest/rpc/address/credential_utxos.sql index 16548ac9..c4f13cb9 100644 --- a/files/grest/rpc/address/credential_utxos.sql +++ b/files/grest/rpc/address/credential_utxos.sql @@ -30,6 +30,6 @@ BEGIN WHERE payment_cred = any(_payment_cred_bytea) AND - tx_in.id IS NULL; + tx_in.tx_out_id IS NULL; END; $$; diff --git a/files/grest/rpc/assets/asset_addresses.sql b/files/grest/rpc/assets/asset_addresses.sql index 74af6fe6..f303abc1 100644 --- a/files/grest/rpc/assets/asset_addresses.sql +++ b/files/grest/rpc/assets/asset_addresses.sql @@ -48,7 +48,7 @@ BEGIN AND txo.index::smallint = tx_in.tx_out_index::smallint WHERE mto.ident = _asset_id - AND tx_in.id IS NULL + AND tx_in.tx_out_id IS NULL ) x GROUP BY x.address; diff --git a/files/grest/rpc/assets/asset_info.sql b/files/grest/rpc/assets/asset_info.sql index b3636ef2..37189072 100644 --- a/files/grest/rpc/assets/asset_info.sql +++ b/files/grest/rpc/assets/asset_info.sql @@ -14,13 +14,63 @@ CREATE OR REPLACE FUNCTION grest.asset_info (_asset_policy text, _asset_name tex ) LANGUAGE PLPGSQL AS $$ +DECLARE + _asset_id_list bigint[]; BEGIN - + + -- find all asset id's based on nested array input + SELECT INTO _asset_id_list ARRAY_AGG(id) + FROM ( + SELECT DISTINCT mu.id + FROM + multi_asset mu + WHERE + mu.policy = DECODE(_asset_policy, 'hex') AND mu.name = DECODE(_asset_name, 'hex') + ) AS tmp; + RETURN QUERY - SELECT grest.asset_info_bulk(array[array[_asset_policy, _asset_name]]); - + + SELECT + ENCODE(ma.policy, 'hex'), + ENCODE(ma.name, 'hex'), + ENCODE(ma.name, 'escape'), + ma.fingerprint, + ENCODE(tx.hash, 'hex'), + aic.total_supply::text, + aic.mint_cnt, + aic.burn_cnt, + EXTRACT(epoch from aic.creation_time)::integer, + metadata.minting_tx_metadata, + CASE WHEN arc.name IS NULL THEN NULL + ELSE + JSON_BUILD_OBJECT( + 'name', arc.name, + 'description', arc.description, + 'ticker', arc.ticker, + 'url', arc.url, + 'logo', arc.logo, + 'decimals', arc.decimals + ) + END + FROM + multi_asset ma + INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + INNER JOIN tx ON tx.id = aic.last_mint_tx_id + LEFT JOIN grest.asset_registry_cache arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name,'hex') + LEFT JOIN LATERAL ( + SELECT + JSONB_OBJECT_AGG( + key::text, + json + ) AS minting_tx_metadata + FROM + tx_metadata tm + WHERE + tm.tx_id = tx.id + ) metadata ON TRUE + WHERE + ma.id = any (_asset_id_list); + END; $$; -COMMENT ON FUNCTION grest.asset_info IS 'Get the information of an asset incl first minting & token registry metadata'; - diff --git a/files/grest/rpc/assets/asset_info_bulk.sql b/files/grest/rpc/assets/asset_info_bulk.sql index 1f6d7424..843386ee 100644 --- a/files/grest/rpc/assets/asset_info_bulk.sql +++ b/files/grest/rpc/assets/asset_info_bulk.sql @@ -60,7 +60,7 @@ BEGIN multi_asset ma INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id INNER JOIN tx ON tx.id = aic.last_mint_tx_id - LEFT JOIN grest.asset_registry_cache arc ON DECODE(arc.asset_policy, 'hex') = ma.policy AND DECODE(arc.asset_name, 'hex') = ma.name + LEFT JOIN grest.asset_registry_cache arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name,'hex') LEFT JOIN LATERAL ( SELECT JSONB_OBJECT_AGG( diff --git a/files/grest/rpc/assets/policy_asset_addresses.sql b/files/grest/rpc/assets/policy_asset_addresses.sql index d0740cab..a0914493 100644 --- a/files/grest/rpc/assets/policy_asset_addresses.sql +++ b/files/grest/rpc/assets/policy_asset_addresses.sql @@ -38,7 +38,7 @@ BEGIN LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id AND txo.index::smallint = tx_in.tx_out_index::smallint WHERE - tx_in.id IS NULL + tx_in.tx_out_id IS NULL ) x GROUP BY x.asset_name, x.address; diff --git a/files/grest/rpc/assets/policy_asset_info.sql b/files/grest/rpc/assets/policy_asset_info.sql index 8c4e6f2c..d479576c 100644 --- a/files/grest/rpc/assets/policy_asset_info.sql +++ b/files/grest/rpc/assets/policy_asset_info.sql @@ -66,7 +66,7 @@ BEGIN multi_asset ma INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id INNER JOIN tx ON tx.id = aic.last_mint_tx_id - LEFT JOIN grest.asset_registry_cache arc ON DECODE(arc.asset_policy, 'hex') = ma.policy AND DECODE(arc.asset_name, 'hex') = ma.name + LEFT JOIN grest.asset_registry_cache arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name, 'hex') LEFT JOIN LATERAL ( SELECT JSONB_OBJECT_AGG( diff --git a/files/grest/rpc/transactions/tx_info.sql b/files/grest/rpc/transactions/tx_info.sql index 4a42e0b1..8328b7f4 100644 --- a/files/grest/rpc/transactions/tx_info.sql +++ b/files/grest/rpc/transactions/tx_info.sql @@ -644,52 +644,75 @@ BEGIN tx_id, JSONB_AGG(data) AS list FROM ( + WITH + all_redeemers AS ( + SELECT + redeemer.id, + redeemer.tx_id, + redeemer.purpose, + redeemer.fee, + redeemer.unit_steps, + redeemer.unit_mem, + rd.hash as rd_hash, + rd.value as rd_value, + script.hash as script_hash, + script.bytes as script_bytes, + script.serialised_size as script_serialised_size, + tx.valid_contract + FROM redeemer + INNER JOIN tx ON redeemer.tx_id = tx.id + INNER JOIN redeemer_data RD ON RD.id = redeemer.redeemer_data_id + INNER JOIN script ON redeemer.script_hash = script.hash + WHERE redeemer.tx_id = ANY (_tx_id_list) + ), + spend_redeemers AS ( + SELECT + DISTINCT ON(redeemer.id) redeemer.id, + INUTXO.address, + IND.hash as ind_hash, + IND.value as ind_value + FROM redeemer + INNER JOIN tx_in ON tx_in.redeemer_id = redeemer.id + INNER JOIN tx_out INUTXO ON INUTXO.tx_id = tx_in.tx_out_id AND INUTXO.index = tx_in.tx_out_index + INNER JOIN datum IND ON IND.hash = INUTXO.data_hash + WHERE redeemer.tx_id = ANY (_tx_id_list) + ) SELECT - redeemer.tx_id, + ar.tx_id, JSONB_BUILD_OBJECT( - 'address', INUTXO.address, - 'script_hash', ENCODE(script.hash, 'hex'), - 'bytecode', ENCODE(script.bytes, 'hex'), - 'size', script.serialised_size, - 'valid_contract', tx.valid_contract, + 'address', + CASE + WHEN ar.purpose = 'spend' THEN + (SELECT address FROM spend_redeemers sr WHERE sr.id = ar.id) + ELSE NULL + END, + 'script_hash', ENCODE(ar.script_hash, 'hex'), + 'bytecode', ENCODE(ar.script_bytes, 'hex'), + 'size', ar.script_serialised_size, + 'valid_contract', ar.valid_contract, 'input', JSONB_BUILD_OBJECT( 'redeemer', JSONB_BUILD_OBJECT( - 'purpose', redeemer.purpose, - 'fee', redeemer.fee::text, + 'purpose', ar.purpose, + 'fee', ar.fee::text, 'unit', JSONB_BUILD_OBJECT( - 'steps', redeemer.unit_steps::text, - 'mem', redeemer.unit_mem::text + 'steps', ar.unit_steps::text, + 'mem', ar.unit_mem::text ), 'datum', JSONB_BUILD_OBJECT( - 'hash', ENCODE(rd.hash, 'hex'), - 'value', rd.value + 'hash', ENCODE(ar.rd_hash, 'hex'), + 'value', ar.rd_value ) ), - 'datum', JSONB_BUILD_OBJECT( - 'hash', ENCODE(ind.hash, 'hex'), - 'value', ind.value - ) - ), - 'output', CASE WHEN outd.hash IS NULL THEN NULL - ELSE - JSONB_BUILD_OBJECT( - 'hash', ENCODE(outd.hash, 'hex'), - 'value', outd.value - ) - END + 'datum', CASE WHEN ar.purpose = 'spend' THEN ( + SELECT JSONB_BUILD_OBJECT( + 'hash', ENCODE(sr.ind_hash, 'hex'), + 'value', sr.ind_value + ) FROM spend_redeemers sr WHERE sr.id = ar.id + ) ELSE NULL END + ) ) AS data FROM - redeemer - INNER JOIN tx ON redeemer.tx_id = tx.id - INNER JOIN redeemer_data RD ON RD.id = redeemer.redeemer_data_id - INNER JOIN script ON redeemer.script_hash = script.hash - INNER JOIN tx_in ON tx_in.redeemer_id = redeemer.id - INNER JOIN tx_out INUTXO ON INUTXO.tx_id = tx_in.tx_out_id AND INUTXO.index = tx_in.tx_out_index - INNER JOIN datum IND ON IND.hash = INUTXO.data_hash - LEFT JOIN tx_out OUTUTXO ON OUTUTXO.tx_id = redeemer.tx_id AND OUTUTXO.address = INUTXO.address - LEFT JOIN datum OUTD ON OUTD.hash = OUTUTXO.data_hash - WHERE - redeemer.tx_id = ANY (_tx_id_list) + all_redeemers ar ) AS tmp GROUP BY tx_id diff --git a/files/grest/rpc/views/asset_token_registry.sql b/files/grest/rpc/views/asset_token_registry.sql index 70b01d89..c376deed 100644 --- a/files/grest/rpc/views/asset_token_registry.sql +++ b/files/grest/rpc/views/asset_token_registry.sql @@ -1,3 +1,5 @@ +DROP VIEW IF EXISTS grest.asset_token_registry; + CREATE VIEW grest.asset_token_registry AS SELECT asset_policy AS policy_id, diff --git a/html/index.html b/html/index.html index 5aa2898c..fcdb96f3 100644 --- a/html/index.html +++ b/html/index.html @@ -5,7 +5,7 @@ Koios API Documentation - + @@ -23,7 +23,7 @@
    - +
    diff --git a/images/Logo-Letter-1.png b/images/Logo-Letter-1.png deleted file mode 100644 index 38c3b31d4cc781f1e1afeeba57932de4f47defc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38031 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

    E<>j);lKC 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 diff --git a/images/Logo-Letter-1b.png b/images/Logo-Letter-1b.png deleted file mode 100644 index 5ed1eba3b591802209833ad002d77c6689830b81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40841 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]*4}EqN653FR*4 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 diff --git a/images/Logo-Letter-1c.png b/images/Logo-Letter-1c.png deleted file mode 100644 index 282b0fe0ddb0a117d0c50d0848d582b94c60fae6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37519 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 diff --git a/images/Logo-Letter-2-cropped.png b/images/Logo-Letter-2-cropped.png deleted file mode 100644 index f8e8f2af343482305916cd17bde850fd967e2965..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6406 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 LHl~k_&qVweFe*VW diff --git a/images/Logo-Letter-2.png b/images/Logo-Letter-2.png deleted file mode 100644 index ebe910c28f72b58e6941a4af7569843588bcea66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11263 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 diff --git a/images/Logo-Letter-2b.png b/images/Logo-Letter-2b.png deleted file mode 100644 index 692be4c2344e1215dd84150b097f3f545d774ff2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13193 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 H?gjlXKMKX5 diff --git a/images/Logo-Letter-2c.png b/images/Logo-Letter-2c.png deleted file mode 100644 index 5cfab6e74f2c5b289052507999003767caf8f361..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12457 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 TkglZvGr^UM*VGFy+zR<$>VgGH diff --git a/images/Logo-black.png b/images/Logo-black.png deleted file mode 100644 index b763bfd7a124311cde3dc25f7ea03586c4133837..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56013 zcmbSy1yqz>+wKS=Eh(UgbjOg=pg44QcXxLxHGl{rAuSC8(%mJ}5&}wz(kb1{+4Fwy z_n!Zp^Z)<)*I}(;4a}bXJUgEIzV7R~c9e>eG%nU7EC>XGD=Pz2gFsNjAdox9827+$ z!sAIv@ByZajIKKbf=z^c-GQWKkU=15#kLwc9y*E&Lgvnn9Hth|W|ka2jxL}z1R^5t z<6>%VZ|Om0W@&BfBuca2(m_LIYavRb&8Ntz=<>qS##YA9%~IV@NyFUF-dxavMqG?a z#778p;ArV#O6B9|;N&jkBTDmEze3)Bd{$n5Db9 zo2`q7t+NvqvPV-hXHO4N8gQ%s8iS+DKixXH|2JEGPUM*WYG`5pPdgV+ zH;2D2Zeb3!bg*=^bntf^V;p}eX{9kna&&U7A1;E@YD*khg|MXrQ9sjw6 zyN9F~7{=cd@}IVL*YI_*gsNG(JA1mBTS|I?+oVN~#zp9bo299Tvzvyqv%|kvO6A|T zOvTN^!AZrWXlicjgd7C(fB1?e%+$kDlm@IDH#;X6J2$@u7q<{6zYs5%-oIKYI$PLU z`ToDH1cZ3_g!uUX-3sg#3sVo%|E`^dxsa8!o1-ZhqphQKeb}#;Wf2n=jE}qU>7vw z=47|9cq+ijDQM0k$Z7fS@57wUJ&}v~@9$fH?{ivkTM3#Anz8ema)R&knF+9)nelV6 zKNYaD60ox5dCDVT`tR>6yV-)L$<*QhyC38;hTH{0GPdqueSH6Z2-GcI|977WX8GJ2Ow|8i z^FaTrJ9jtr`tR@if3|Y})qVf(9ryot-+xc2xs9olwIv9QP#WY&pvVaFujPRL-z)Xk zXa6DE{JS{_VaT`t1W)kCKjGKX33TlSf@_i|p9};tH6#mruHlohv)~_~?wKWi>;0aD zKvgLz#)C|@Tt~|#%?pdhPPXZF_3Hv1V%@rXGvvkg2i1Gt^U2BA88_8NOxrj=SB2R% zNm6s_J1aLxZ1o-x*rkt#sL3mCajvRyp)-@(_Fvyp6&2cYtRD)d&{eGeev7`ck}-Bc zw>hvL;Itn%uzf-ZYk2Nw4N2@tAw&H`{}~bx#YIK!5;|ZpK}$*X7J*xmtevadsD1vXf(x1Rrgw&9WO!uB&^^ zAskNBu7MBTNGU!JeXa4?G1D-S%gHG@7S1H-pO6G+YI8C21mk}JyHCIVMmopI;V~Ow zcDs{parHOm>;8T0hQ;FvR8O{1a%YLf%xe6S0Q2va_+uXX%EV4K$D`7`=C|}-Pj|d! zz8h~WLoOt4GwQW>IpjyOwPfh7tltpTOrFGz7<%5AyKC3AYihIne*Da|y%%-ci;bwg z*F?vgxlU`3#WdAOMAgHb=;OCO<6>5vRVv}=c9>Iuo6%_+{kP@QgO2X*!0+d3tLmW5 zCid&Q-h~cfZG>@qVjHb)KkmlpF_L~#DDkfSYCOy8x_^lK5Y|HdUXU;T4CYf zkCL_G89^Sr+3SwM0#`WY| zgX)-kKOg;UwS4h24EACbs?rN}U2lV;QwTg*`&hFw{UbfJ?N4W9=@rG%m3|iQugIQ@ z;FsgiR7JU9y0iPJ8?%c#)H~j?-I6y?_(gpD2Z*xEc&6Zgj@m@CIvvTEzT0dBDSf*n z3JvV4SUfM@Yzll3XqzkMJNh~;&h){9e z-@o;C>|{8O%&i0F?*DC%8-d$Y=jAN_okZ0;^951F7m@O(xyvy}j2{)|g<8J!#ww2z zWvx&DFhrl;-6-7|^SiQz5~%i{etyKYiIyDu;q?Z2Iyu7K^~>kIoIF9(GJjjmEP%wze*>tnxSa?%px_Z{`8FBV;Ye+~}_9QjfA&3^h* ztj{DdGv4pdu|%hv^3um+CC+L%n6wS<;L*m*_FR_XaneDctQDOGw)0pEU#?vCrEoGPdK>B>^1a4Z@EuWB z@4V+77VO=x&;+f&47N-Swk5X6ww=CfuAmC;LeVdl>p*y0HL@N|DvNt=XQDUt1_|a2 z_H_O(ed_!Pe=;w3XuBl;$fqQqt67tQhtOpM=8 z9di9tbT{jG#w-yyd)p(JKi?H0#PBtQgt~mFS0(lb;z%4ZSyA0$Ih4F+a<$;Qo>_0Q z8Nni)pC}2o1AB^}L%#V+a_ZZO&9h9~3;Kvll!Ez{^HVPQ8>;#n+qn?xM71|~t=;*L zS=VX^E>>NGvSY=Jb0ZoZgMX||O#;QOWlQObYI( z^}H7-Rgco6r}nJU+MP=HOKoy;P>Eo99|FV6WN~6p2r-s)XX#ILq&>Uk99S?~QEob( znF@*`B4^wpvS=+EGz?+WAICx5_$U`u*dg4F;*fv5jKpnIG5L4Iqg{ zLzM(IQxwnmea=-z5xzeH1&5#%{BF%!>v5WCd<7sA^O($qw@au<#PYGA2zx)5H0|g_ zrfL8pOr;YqXi$Z38Y~~MmmPI9OC)(Rp#lhnCIri0rXvh~5ekiEmlV&5^cJ$e&W_aQ zUO>dOwCssrwK>%m>`%dO^GQ^VO*)_Di@kht9~rrRU;UWxn)q^i=3iRT#5@j-Fgca`;?H-Tap-&CMeR)tm+uuD4ampyByDl2^$NtIC z6%)|`wnJd)C*IxmjMdvY8r&qjx~YXcedELXlsvi4>An+n8RumZoCMo1yJ!{|t(B_> zn`#VFu3p=&TBHwr!FFhBcRRQpi@iRQ?fvxR3qe#z5APwjx9yA(xu&^BYQRg7ZsHQR zUXFiiwqe-D#^IaLV`H=Je^Xqqf*>$w6LQP5v~9`#^@}hM>d3f-jtqV6w4Mv3tygTNq+UZ;<0Lf{Dl$jmN>%`^~c*HLn~qNA$xu;Wq32BGjPn z-p@^5yi@$Fm^M!~J(;i2+V?0!GU2Zx&F*WrDi}9Rv3d-$?5etC3r#~q*W{%m4c}1B zCuyJTwTv{ZFFFxqu^Qsh%(ly&V%O^xPk1tc;C*}Ewn>1~w!I@;fuQ~4x%{!TtgP%! zV)AUMX0M*ICl>7M}SxU;?#BZCK9o+Y-j-}zmF1=k-O zHFJpPbhVQ)8=DnFx#!s%5x>v*4HG&I`u(A%FucDx!;1xT0)E7JZa((~GruG)|TX$NW{ZL;SV{t_6zC#&nMKQji zRb)bHYHGtlW~?VNN#LFOePzYZja%N6c>XBhrRsfSlf)iUveiFmQiAj0C>uB(xe%cq zNsJFX24Vkt!;iRK=#;LNWqq%jkS6Y^xgvV!nBZ6_CMkv!;KocfKjoS~^UlH;$taUHm130q{ z6hzmIW14hmPI&dfd4ZE=Ri#`I8G;=@B0jUv4PL3#casfXNV~&`1E|r0X?fHIv25OB zYGFP7!Ebra@l44kQ7yqZM5gz?zI$mZW)%uF4S>Ywd1zrm9J9YpC0cEtTlWMN?RQ*9 zk!W{p{DIivF9`Wn-^hHm)UNxb|GqjK6iW_jlbASrV!zhcR$o@2XOPD7e_$C$ZM@N! z^8AJ4A9gKQ<$1{EvK@lnou%GgRWGN#9e%oh{2kbEq! zwgDDQ^4)Z|UIlwB`cfNH!Ep)IJVT2@O4kVK$~!+He((Vsds5tv-jCi-AiW8OjG&BQ zVxHAepv9l;3|+Q0A(oSqSVTn4{vmydT38_D{<;X{L^WjX%Jr#7kgO1mDlK<i(7?3s|7RDLB=<2XJ2xL^s28VteF zlX~1SzeuC+&IzJz!bdYql6_qJK8Ut8o}#hFqy0wn&SUO)r31-Xo2A-wiIh+QkOkzz z5b{ldBJC9;6lKm(G6eRY_RAD>gH~t7;2*?$o*NZg3*PC@V{HqBmEYak8;bU8$TQ;M zlw7IA#$lvF3s8eFGpSJwPXR7--h#v+Mm>W-MDWV*r6{pIo1XPIrPdaDeS4J7*U(y* zK~|QJq;n2O-$s?$%>0B|>RlsbzLSlZJWs!F^O`g-ED{UkS)o*JoOzbj_cWcls7Vr) zfz-6}`9jzEw-dT=^eeS~?QT7x-CXk>!Kca8S3G%LJE=8pcH*-S>#r~yatAlQasI|R zc@?~YDP?&78gUkLrGOYeluiudo|>5v4vQ%(zlryrZ*+yhcm=4r!f^;8Y(Kcj>!+*Q zs%Yl7`P+`4^_sLGYsg=f)Dn>*lg2YSn55x>&n$kcS8ySVqYDnXhQ*7h2g4lQ$<^#J zQ4(<(O*Eq6&1pX_RQj*HfdlL2+q2{U;e@E_Mwuub|4wtJ&Xiq;iVt z)A@@oD52X@vxT~=?WfM8mBCCk!FHHeR&7{XihKhXRKdNO`@5>g(hLnJ+&nxy&SQ(p zrL_A4?co}m;UnDQWkD7oecx+aM2=|!ZYukS$;`*5JLZFG1 zYG^`IMt+B~kc2+{$9mFbR>7LJi$rc^ZT1Pc^ISBKROPLkm8NJq#OG8bzUxK=#(0g+ znAe9+OpoX1)4lsyGbZ-CXZPx$pHetHuwad6YIk98p(Rsd$l|AY&m6g3Z{f`^dFZF; z+U)6@pqxdXu_NLwQE0RO+v2UEi^V6-`d8Glq~4AC4jt2`k5yAZw|!#RcaV5d8} zwqmCA*0K0UaJ;biY{^i#I=C}JT+*BlQGfB1FKtw3J2$n)XM`LQZ%L^M#oh_`%80ThO61=VV6o}f?r7fu2uaVN+>UNvJq1iA&s}hGaVptj|m~r$bBoL<0 z2b7#_Q*g9_$)sCPv3zpkGxm}`;XQBf4zw<@SCXj)M&wQ{B}Yrl+>vWmS4WAJa=%DQ ztRTM*iBHOwj*$4}MFDL!JTGe7xd#;D~ma-Gn2A5iWkGOjc zOaPTJH#^H@Tec1;t;wzGYHqHpL1xiwDPNhsD0Tu+D5tYdh}{Ob6TOIFIw0K?MQ?#T zXkD-L6}VN@{xT+Uif8hA@r|3AGxh4cQMJ-oc3w`qt{esJ6fK|vV&?Invo?5wcvb6w zCb}=k^L+4y6K%3M$p!wCa^oPupZx+Go+J)={DV-50@CdF*bF-8?GxCXDvh>0cGq{X zYFiG>N{EDz#liq`};6?Fjuzp!B1=1Uo|5`iyrWCw&2#_?;Ur1OTm(QiqKG z^X97%)(4)%zhT=y`0c-VgWbZSPW<`(G8{f5hzFrYBhMjGr#E#w&EEp`G)ddh9V*$f zu!b*2HN7uLNphEuM=+n+Jg=zHX~Ky5q5$I!Tu`e(T*4tzgf*l&l~2s_9U*=(12HZz zzi2*?O&5Bl*N-K2Ns=v&7IIO@qyPw%h*~#3#6*mN9z!D@aVD6|=xQ{)iO_p>1*l0} z0@a?0I$2#w>~y{n;t-8-1HyZ(H+S7XX5(Ev3NfNf2}4Hazlm*Uw^O=wsS9HIpWVMu zjFlfh&EH1=u;N1f$~3$UL%E)kIu|?mE9h$|Jcb*IQ33Oi!f`xg%R%pK2e$W~bx`&T zn8&0%-d)F#hi|jY=xKg%A<&2j#>)nazN(-`5S`LE_%>Mu70*Nzy~zQ9C=(Ix`&%wO zGT_G3uKBjAsT2-xw{;bS!#~L==j65E3EiC8);jBn>=vM3USczXtVu+GweQ&BR z91KwlqYxf!@SMg#ID>+;w$4o(lRy=WLB8@{+}qR=1E=z~B|=>!!@FnLP93V~6Xdh%&Q=g)dl-dOVK)(RduGRWUZp zY#+m8_cbzQ;r+meq%<~C(;9`-4O?>Ih&VvP)HXw+ICwiT8Y;Wdn50cGSqu^ZL{0?b zF%oi0RMb_vvl0_P5K(NuSuoyi%N67Cqtq(}Y$@NPsKXEvBghDBp|TFUJS*YU8~V~C9;7M>fll^)FCf|$l7S1u;tS!h2Nr-C3pgmc#r8XGo zr4&*l+0)74vi8|}U%8IK&x-ntT}@~kx342-wX>N*zkBDAK-h?a>6UNf`7?V}O~zx= zuU~)R`y2e6)q=E1?n*L!OEqs@F%R3rh44kSW8~f7O$7U-As}u!^4(`Lc3|IVyw4f8 zS<3e}Bs0t25HigRR9AA^&3|7Ejm@+iu-DUlutUGdFHsDe#CdKIGZ>M|TyVy8f06jp zoqK^{9}0@gh^imMf_#ccEMUf#)BBf>x&}Uq#t+o>zk8`!@^}0r_q}imw z!C^MNOQf#-=F`$?)}DJH#VJH@;+W?8cEuK*KsP+36{vC!a=)S?5;SYS zzRM7edgrE~JxPte9RPt-Z`15-ip@CRbRY#hiToWGrRxH7c@P6w-!^-8$&h|Ply4?Z z_@`B5JnfpWS<-m4M+T|;@<{kcwViwiq*dEM+^N$h(WKX)Sj~g4?nrL(k3QF64lg@I z(84h6CB=%wW?`K(UVx~{PLakkN#dQa5*nk7#9s`pku^F{=sp}(PqDJU#N~zP#cajI z6T|wa-Au_tTz>h9QHKzGX~(!pQJ7Q%a!54Qwj>0`{=FfuJgG(Q)kwd2xw*f&q`WR- zT4y>}G~qFfzvmk!-oD2)1~ns1lWV~Ut%B;7!|Xo3mPx{3n1+{TxRXG zAvO#{(uJP^(j*|DSI+k7cpvjM82@bGl=k`ATfzJh0*Bv78aOOmtg90=R%9(*i_5e; zAjHhGdMYU*Mb@hb(?D#bRkCLUv46-2+f!nw4&if#HEwfql_fnbi-7aOQN!%oMDr&5yJev=P;3po(fsF?jl#5d9_$`C1&f z(0=y^zy7(+OVSS=xxaD`RVrQzG}pax$}}Xd7p6v%l*ldZtfyP2XD1MgE@nV+7`JxT zAo6{`p2szQ54SghWDn(d2>@#vr*%s;1HvZ@fnS+TyR7~O1XeEZ=Z2i z%|N=`KXxq^3b(OX9Z4dbEhdg6Yn!y3`^S zKU%+Y2~dwG2KQ4uG`)}dR_EqKFbXa);(|Zak(6|jZ)jjM$Yxk=aWFwIuaP;bA&!KUO!`Z#_frz7*cnB0d&bg8}_=+Q7&mP9z-vAu&Uy1!u~?#*fE z%&5v0Q}~=JCyNzoBe^_(LNQXNYYmJlMvy1eDY=r9`;Fknfw%K{> zR_-EUP1KZeGY&m9S&~(7YL`?CMnjHX7!;Nn--hIyX>#A(=hysH!8XpvqpA>l5jGvN zZcv#im0zS21=m~pIGO)lIKoM~DKMMQ4@i`E162^IEW?^@S57r9pTN#smF$xo!p6eV zpp0z!eRC#~v*>>N3him#7!>2|`_;WxceP22AOfog>^o=S^Nka0LP+wUaj7=tNwKi| z>y);vFbz53?KZ5+FTtQJTOK;iFDi8*EtJW{w?9A&8Ci5 zlTTAz7E^hw-EvOnfZXiXK}oHNu^D-_b41EknYF*Wd1ij_SN)gxbTX#$q`X;;!?EmJ z@Z6VS4JW{fTzLwEDFBEU;RDx0l-wWMyR&mh8cF_&sf}dHenDhV5jbE}a3~ zN^3nSw>396ZFQHf-!*?l0w*QKQNKYDosljL6y#|ipE`Se5siHIjo}6G6fA;BDBFD` z_-)KzcldSu3schYIY9m!aHMEEl-O{mCdlX|3eZG9o-D{|&uVUNeysbBQNJKDVX{!M z=4-eV>VU7>9I|S9+#eob=rcwu{ifDRIh#Ys@XhZHBxilH!!s707Rh2`FHU?YjFF*P zy|E`L2#c|~Jl>rCf#)?C1N(X6_d;yt4`rArV15xW0lt}%xL)61tj>**iM3O9aLjL8 zB%9qh-WiD=cY)@w!$?1pNx74FlLaQr5`MN5iJhd>7{jC19ySwdXg%yKeIqY~(0zc^ zSF(mut1^_qFckC~oIE4^6qRoCuP@~S7qZ_sa>eW!CFV&U*CMHIY)zDC-?2{>2CeUp zWSZDTm*$A7H7|aW8aL8XK)2n^4GAxa7!F5)>c7yVP@B1P9N{mc2(3J}cXm!$pr_*P zzUyD6+oI)Rpv)&1F%9j&`7T~mOQ)gs3nuqs&tHp`!qorkw!%ov*SJ(mGq*-%ilYdB zxMIIuogON*6Iu8sv~sm35uQlPPITb0ZpWx>pRuj=?^N|uLM=R>lRh9#+3*tHGDpG* zO&F}9^%5R~Q%Qv#GdKSEvy`%-p<&0I;JX0}7G~y>_;e_9$7NZ@Oyd$XK5@eEbzvcc z*o2r_^P@LUL?3Zc(74`tTTnJ>ilzN)plneE@JG$hF)`=k-=?IJCx23m4hXw!9?inJ z>WXdk@tNf(FYA(Yx*0RhhiWKMycnj8>h&lGiA-Xeo_uTDc6wMhcarDOKUs~?*~kCI zh*`Sg)T{beCNt?QhQy3|^D93tOLBh2F>U6mY@-D19Q0%Nw2kKCVNJP@wVkajxbKTD zc404F6i+%7#GQ|B zR}16gU33(w1(otjhFT`GnyEVP^cVwn>t+VqjP^rF#>kx-yUHYkl5C3k_v*-PnKI^M zO56f1OUh6~SwHY;#S_1-WY^H62R|&>ge>h(r5XZQ0TL)f2@r65eLgO(NHSdVJrW;%Z4J(yFO9 zzS3`~G{oW#Do^!Pt=yS>msYR0DJNKzY)GuSeKKQQK~O_kip}*{)QYR~*~_lDfc}*rn_!s+hnbO)(G1(!$?211Xqk+bhJgd_ z*(K6fbG#5?YL)q6K7sA)`INmv*%g;7LBvlNfyZX2>R0s|YG_N0-bPOO@1d=&^Gq`n z6NIjmg0y~NAJlW;n9I0!-xJy4CI&^x$e+vp;5KeY8 z@Y%e>Vh>Y5cNucjO-wJG=goiZipx**8A_>D`(}`8xjxa-qb3L$Uu9M#pX{A##t6PL zgFDW{dCQdX_p~$y{7-Q&*LrvK>wIg}d!62V&{rjl-aaEze-tgtNQOy|N=jW4U(+qC zYcBm{C?Q_v+MmRoIzmWm%OO@}6CSI>2(!XI#b+QvZyWu!Ok?4CFb^ybBwU+1%>lB}b4#lV_G`|fB>Ao2sifVbZcWs7Z8FexGYmbK5 ze;E$w3%aBTk`mvfLv-fVoE4NrpSSO053}ead=eA0RmR(IP#;8IjpKM_mT+f(#SpvP z$XiQyY963D5is_7;axn_XH*G;IW?rKsj0mmsd*N}KySX9KNps-yDu~*9!82vUH^HhV^;%AvW8#*L zT3tMEP(Eit&Z=?(W|1~zL{9;`t;joz*W@I4%|AD9ahp_r@-f(&mLmP2VW6x4?Q4bQiU8h=Z2nsu18Y z`Vo*KDR7zd6gU&`PGJIQt2~

    YQBV`*$F@=vduM?|=l*U-5_MiZl#%gZ;UrgV|-I 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

    t3~ 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

    pm0xQl6 z`o03}fT?Z&v)^{7S^;Iv?sy!4G>_wcn22ycEoaDV+PSg9h5A7QL&Kr6P11hHi!$Y^)^$?+`>l;WIuB6u-A)nn*nBggy9YZ^c2 zvXF132^q{6(+xf2sk}N%e6$1}^ zyBZz4F&-s4lmk3D?hwDn^w1m6OyIL-`?DJJIVzJi@a>#^=rAId0UVg_s;HeT9tuIl zh>aEBgTSqSDUof$4pwGnql9?Xik67Ynfi9ZJB%0HEdN;p_%=^UhfW}Jm^#gC=mNJu zMPFY?TRM~YIZz}lsj?yMDGx;8kb7nDC+{zJJ>Nr97Xn2Udu}ANv1a;-AEw+C&I@Q7 zC&?(1a2TB65&!sfk{^Rs;2#ok5Kibp#OJ zSbv=PNJ*kND<=Roe(AqN?H!N%aSAcr)keuDmZEJBEQ_O92mCodP<31=t~)QAg6j^U zB0-T|B-&a(?eMG8HvNN1_w-u3Y(}eY*Qm*gw5o`f$vgAdw5NEox%1)0OG0@5eZzg3 zpYWK1f#3f3>zG<-avf#=t@I3oVauD33@t?B3R(K->(dTCq~yH&Kn@##gh8A#kP*?; z+kT7C{tM`MC`a`1b^<6ewnaJJhZlnOISxV#OIFhb4zZuVN$>UFNd#EsWtpyouMjEM zt#&zX^;U(h0CnHBcR2e#mvjh&f2naHc_L5F{t&}-3wBo(BW&7tBWwbwzQ)QT$ynSk z)pQ!e?u0xvc%IxW_;*E}u^5#)!X^ce(@uutIsnp~%GLjU~=DhZgU^W5^oXq$IoeF^kI zXk+{OMfATc;-zz*&`<30%u-ck}F$QD;)HB~gCQzL((`Mx;N{pNS*gmDA3t<*|0S!wQFyF;p zoJXfV85j)jwWa_YuMpGvQEzX=T6dZKo(JTHI?Ir znwxW6St`jk&zS-i2TSJNg_&~HY+@L7cunDI6Yc?3e7=;F^IH2Lhgt_LBLQ#TItK!T z>%FL~q4_Xrb9nhj3wpqCuL3b)ya3OG&g!$&D3;kN`Tsj1JeH`PQtXY;moJ;S@XyiE z-N4$0DmAh(Qk>(#5-0rk$Ao7Z?xJ=3IG+GNbs!#FusRCs`j9mi(Q>kUS2t5h!}wOv zMre$@*EBloI+e57(?y_wfP>?lu^_cQ8e+#wLyfp8PnMEu;4;OUiJ&4D9R(O`V0Z!o zBC3Bm==`IV9ooo3z+uSz7HsjMv4S{PcT)qTS*Aar?g6bXwD4{^*D>)Yc>3Z65-E}% z;XFiaTm;8Ce)R?+r8I2jvu9pw%L9LbI{hW=P9QTiiv$l^gFXWYqPCyvwD(@oZ=f8Z zehn@>Jl}IHc$s&b>tCd;454_!!Alt7ExhzivNTYY zJ5KAYBl{)}&=)i(5ub;KdC6)7aleqUj9BSy|8Epwogz|e)*^R^3($=xT^}wjJE6l` zyk(-;T9;fWdl%WmXH^(W1RWWoKE#5fFr&-yiXA`LP_f7y2yXa{eiwd?cwNMD*!9k< zlA-4eL=P>;abL8>>2d(Y3{CD}1E@|@PG^1pDT@&4`**GCboV3)SgV$=)Y~T>ZLTXy z-02z8_zE9=e(`So5UcgTxDuRFgDMK84Ig4`7Luebf@9bQgx46OB+d`dscWgrdn(-p zE;XKFFk~`aS3W$nD}n4jMy21A=6Y0zyXVVfW-}e0>e91pWGNx5;BaDbPNxs-76PcR}ur_v5egNL~|Qq=brylD$4bx`hxmP z@<9D`@D<;ew^UODVcJyhj{! zH7ANUU@`al4LD_%EMBn5N8vGf0$JO}YNHB?ogk+rKJh}*C)H9al4MWNU2$AabNUP0e1^5VRni&t7ZlS6NNx_}FHzHVY#5HmLB=9m^K6)WMlv=#JGl*) z_YZ*|^a?0UJMLJf033wx{Cw5dswiJE-3@_Q^4d%gBn_%VJG zUZ3Q7Pu8{UEg4o*wctygSyiCRp(!;^k;qR?iJ=RxLPSjJZZ`UypzXyf2!R?JSF?#WA_Nkt;B~0wER($Iz+&F%h=+tQu5FAMu^kOH;!4 zGaemP%`(*dA?`~OU|&Xd7kBD{iRkTu5vIXkSL6uM+rdb&ZFRJeOL-;oeFghBkjMfC zJflc5s&Ybj`8-W+_sJ6_Nd6_$EA;Za>hZ-{zRxo1cja}>XMGD8fTkuo@f*SKVgsVS zfTNt>8TBl)-^IL?G&$RrbiIL^deajO#aqL zbIHYTF?2Se_o7^VfW}+B8~V*Q3VvSvYRkGCS54s0sA0YBuAK~>$kD=M^*7BTJGBiQ zk;n0+|2&JbQ98dxy!zjg5_lHl&j`BW3&)#1;lI;*X@=J$rg3tnZA>3p8c+=5Drt-7 zt_b`;mcB9|s_y&x&QK$bwA9eu-9sZ%$|E4%At~Jq4bt5yf|PW32}py2goHttbn{+* z|M$cFG7K|k?%n5{z4qB_EwNR(yZekSd;DeIU~=_cP-glund^O6Z^$cCT8NED4_ul_ zj&1rDlUfaaKacq*tWZ6~g1M+H{8aT&N_jWa=P@)y0Q*!t-p{)soB0Xg0K?FxV8 z6-7YQu_u=BzPe0}8`Q$41if=RKyFIkEU#ov)?+4q{R|5Hrt0*uqwW9`N=5LyLC&#V zga?8UUE}BX((=-OJ_c%TDMd>-u>~ALlcE2zBE<_sA5#(mIsZ`nRHHg=-18Y%ZLY{h zZ?*g>vdu2cvY!+x9lLu*>2Pd*2*$mLHB#r<_bXE6q!o(#y* zg9W0Q9TW(CczDkzpjn#Xg1>WZjs<|+uqiih2QZN&#|4*~0Rhd?ZJEV0$;c(}ae4%Y zO|E|MuaYg5?0frv0Kz5?AB;ejei=A`sHB=X_DR?)q#oGk#C0bw5I52RC0ZY8duV|g z;O8pue**&?mDipm)%P8qg(Ly)0XkAj>n+3xD7H2*KMHr(ZMnn`A z`UUXUZV?Wm7oPBFgcoHbCF5x@acz6)69yG9%pQBv55QcMDAx zl+mR;e3R5D8BHD4G!mESE|{X|g@RtgDdvgf}3q@q170(5Z(>+RyM zv)f)5aPj>A4|@)8AuyyL4x@&J&Y=pm9Wp}JfEx6d2EG@>AY;ij6y*GY@djOgCr=8K ztkaHboc8B_f|LMOL-;UZGW;a7w=7}%T5HfY>xhDBa28_(c5tSBYzAWxW-9U|6clLG z6>^qMpG^*p^%(~<#79fGqyC5j7&3_~i6fW}Fj!{>8Hht5$sc5xzpcECXj7JEde1bA zr5NnU*>;X&39g>1;0PedUwwTD_@6TmDKXO;s38_oP!koQei;QK+QfDjnWE0yYk4Tj zSAWsl;pp}9)-%n}o;ms1$6Y1>TRdoL(seBY-;j*#z#^oz z>3E{%&&A@cuHWT*(hq6+U_*KUZre5vzVaNT)oSZ^(9t$5Nx;5QBupwHxgir(Ja)jJ$9n>LB0j=Y%=~U<3&_nQuwa)V=S}^z zy56-tEgx;c*J27hqD`IfIGF}dB}@NRqEU+)K1#GctDdRn`s%_nn6?e>J2~&FXn>x_ zWz~4Mpi=3NjBN|mKkZjIC=O+3Z}q%^M`CHMUwj?cw8Em}hmZ}Jsc!8P&6Oy5!9}-2 z_3rB#K60<{58(DlG@i$E+?JU30yL@U8t0wH>2F4Ck8gq-#`lW>p8>FZk}8?xuBC6d zwv!s8hR$yXFdVCg7OXlgS^VMQAWell%7mEhlTr$!XHTXuWQB7|iheQ!H)ttS_P;D& ztbUq-h%1Y}i%N4IuK-qNM5xJxqK-RNzbOhL{C$xYWtg#ov&Vt4Y=J6ff^;a7qULE_d=Tf4PIcS#Ps<{TZ;o`S_%tW zxE>x4s5>xA-OCUa_#@(u+-QVmp>2GNB*G8;{WFT=hEWMig zJ-)~{g>Pp&55GWCHCO;61$`_zKTCkQPE13osw&_X*g0ll!%qp`o<>^9))#Md4>p)i zMHfkP-Cg2K>`p;?GZ8qBLeTfr+1eX*kY!6V=XsUR?{E24pJhNP_YI*8CjFoR2P*-$ z!^!X!);Lh_qy-Zd$nDJGvHROHEx_sanOVh0k4X)-pKm-9|Iuvrrmagy#hlNAY!2EB=;W*)Bh^zS$BDo2&HtkQ$!$n)2>l3Q zK6OqMi;;Vq#Rx9sDO^VLc8kxM! znieYK1<)5~o4!5|ciCoq5jy+e{HJ!}wHmOJafk*RX`QNK{ zbFwxflJ-E;^+#*1zA!oBsqN#(iloE)Pt>_>4|g(7@d(v$k~c^`zF47Gr-F@zdnH2x zr5;E+Y}~UK+p6eP{jW!1nm_p;JyVUczkGjP4E6|4kBWPuf9Gjz4M1P)#Rt@Bi!6}0 zd@Y7Gx=UJAAC^uA{Q}uOqI&qA@1YSr@IL$7EM4eN6=`}&3)IXwr`<8V{1h?6x(2d+ z4Q+A1LwSDwjB^h6XE$BzqhovQ-v>FM3;?Zo?i-FYLsTo+x8Z^llXF57+Jsn3b(|WB zud;ogP`+2Ip{=QadOfE!{JI+!4h#&pyyQaoDPQgrdRxcaPMGL(5(xVIo-<(8Gxn%z z!(cr12^O0S?S>>-6tZ+p#c{HOp99Gp8Kg6&Vmd%f5=P+{R`4WbNA7h#8c|TeljM

    |iy9IX31&Nr`?^&Ip+?QdDOJ@X z0y_N4U=zj-x1fayqzPo?=tkTRVhmW|2;F1%%z&I#Oa^HTXeABUD^0gwmU* zMlHwM{Z9)2tOvKIN=@;Fc3suN{Q3zmZAg#> z(w~@^I1oK0)(Wt+m;p(-py!qKeekO0t0V;*$CRHp#uWIpZ-sqt!Tj0~pAMhH1!46R z%4dRM!i)scLi|mLp*;vBS-?}&xn^fY+0e{hPPIKYmZMP@c+YYxx?L^4E`CdG>l>!* zTqUgBva?`(Z}f5mnIyfi1+ySdCl8Q70z=(+yZPUK%taJp%pUDW9`#z&qfjMBB1!Yr z@DShV@pim4HT!4|DAD^^*QH&udS7&M0)M6^6_~x6_H8{yp5_m-`$KIa-hbT-PJO*9 zwJDFKDAhjH;3X|;78bWg(Ek;x$eSQffHnDE1RhLmA`ZadJSp0Fh%r;y&`1P;k#Psd z+!9X49v&MALio6-U;wR#lGHCE8F*4(gH-l@MPK&GiAmDcz=|aSen-KAn_Mz zUZbJ>AbAO8ET}AA62*>Ulz*D}^*GJnx+OXVoSmZQ|{EKB9wiixStgt@!eGC6wOD1G8O!@Tz z%I~vzBV#lclCNT(#Jzu>)bQ2kn%afXVT09k5dJ<|#DI%_@BBCP_McsriYqY}y<}(` zR`_C(DEruj#85_lS2my>A^S8$3$-8wt+f+HySt*Gr!+RQNUhY}T4wb>aX}D^zW?L_ zQ1-oX|4{h57SpQ6(~ju(}Wxp)v-mQ8^$1P^Aqe2w|emgF0=| zIE06erKvMOKCr*Pzb?KLO5q-+N+I(z((ifHFHLoKCXaxLWl?E3oH$efJqfiS6|Hat z)2)H;#$MjRL>*m({Hu=|8>9?7%~H`>o+u%+_c63wFvwimeR{87CJdSd(IgC&d>rOZ zJ9y8=Fh&3#0zTIn5KxU4du7s;G*h7g?Ewf0P?4zq@txefBodVG#26VZeL+i8O*qd9 zBVfN?3*#8AlfVIRrHfQaISEK5R&a|wzo5zF3PF!}rdqA*Ht<#T+Bl*?PO$1X$Pw66 zi(Y9`hlaGOptO0S%Rv=v|F0I`hBiF+32U(`^FpKvmQlD1F~sUJGBT6Ac)Rs0C9>cX zI^op$zpFGw7TK@(S|DJ`a8pRH8tCF{Hm=dlg5+RKeaVf*t)T=1~p>ZJH0j zL)&k%idbzA*q0DXIm)}aplf!zog8pjl89QJDT;h=k9`YJq?~#9-U|Ai@RNcXYwClv z?V!wpuj3`^{S1NgSn|!)D{d$!hsRlIy|gjQTAC1I{fE6ySjCCk@{(k(z#=D1IySH{R61j`LduS~<8_l0k#|4m#q3ZL`Oya=P3oS)_Rv^srX(uK&mJUpJ- z4gn3o5u`Hv8Y*n-9DUxj7tNhjb@-z#wZ2!X*Tx!dl5CU%XR|PYA?eV=@n6y+)cI~K z=(fh3d6W)6xEFc7$nL=g&{RcVW&{QR!m>c9oX92yL|>PO&gmcGRi0nr#HROfyeq`b6M0x7xTTD zkIV@OET}5#sO(G~nbN&Pd_xl@w(dH4!;G-rzJWj!Pj6sd4T1pIpAFlDBxStok*~wa zy=@)J#wio2k%0{cW?Y`xyGF{}wQP-QDp`MP6i&vuh6mI0J17p;6==opY!=h`mMdGF zcuvcpI<-iCz=Yd@CbbqMn>iO)C;bZ zA)~^@9AyEfQ`y4 z4%r{kF4vpfLEyj9x5Jy~Lw4_9M!S#ZD_Xzs<^ALOWZ12-;?>ev@xPw)%2WgG@GI#^ zZegGP&&|^{C!8IPmcqE5Cqb{%Fbe0{4>q~VNU#3QvBpsQ&;54lYTcZ(>DGrLUV28< ziumQv8!fdaWMV~=rl5<5nD$4+iyV$65;^%5Wi384&nqqJRMYJ7o8FYmnruV-G1;J` zJ4S)qP7{@Xn*(~`{W*QmASm38pdJvxFBqPIB>cne#VKg9>0?m^>9rW`+{Gf01dJx> z&4JOcwQC0hN~6CelX+M8$$^?18vRnw$ddmXdBnJ_lb1* zj#0IvQlcttL~Ybc(k`RDcBxbTH4TAP;T1Y75)fRm~ieL6_BM>=w{$Yd;byH}?hMk0eS%!bKg5GXn^*TXQqRdZ)x)OfOd* zFV^pJr^QRKJz+`^i(-CdTE~oibmXXGH~L58jO?Uf?%Qvw2A5@-KhENx-8yW4KvI6x zrfqy0J4hEATT-p(L(gQWofuv|qsZH)bj<1yctt85bj%$9<(6!F243h17k+4b`O4*} zR&-VG@&{th_hUtTvVfJ$EK`-XJE7Eg{9tF7Y>TMt}@{4zaL#<|iA; zmB56u0YME9fyH_4OqxX0kZ*bXcp)rgs1BAqRM14xS}&*5RzDU!?>zp%+1>AYAsMwJ zeLssc%3bG#I%zu1?KHGCVt%tYa9==H@}Ki{tM#vJjRyhx)O(xJ!?iscz-&!tu*lTP^oq?+*sUbLuWf9QMGLx$m3 zver<7NWqOCr4_r^{r@v_J-WHu1l(d^@QsA&yOgro02+LBQ7jBmH?>)u{!8{L$=v_m zym`k3&;2&V-}G|X&Asu-*RN;Kl>}xS!c|Fd>HdU>Vj*M57=NbZ&Pc~aJ!zAhrBVn} zAb1NCe;9Lv4bG*La&=+V=e41T%Kfx08(v+sJ;a`)`)Ba|gjVgsxu5&6XUN(xqpoSN zpLkUdG?^j!9q;*ot}IjP3_1)V>$xe@lbX&@UYJh2Tm4s>Z$8j!ZPjp0k+vFCdU%i1 zB;7n1d+LGN4oQg-X(sc$-L~fS+_SMA3NgN?mGWz+wYZ6Njs6#AX{@&M%=TSIQmMU< zwY3AMMy_+$v)>qr3cEv z&22LT$iMt*eH2yg$8>;hp(rS-78@xbw7EGHBYHF38}jp|MpOa@aCUY^s=t(@zWJ8VoOIvYbyC2J z+V;j_Ty!&L!Ols^xHMKE!U_?oE>ya}G7tfGdHHH&3{u~3604lBv2&(weQ8PjrcDyA{qK8Y? zXQd^vbXHnnt^OG(dGD?`Zf_bTz5ce2JGvP+hfYVv4dI$fBY!n-(gWL!DX z6TLNgK?kruTpbzzn~ZpP@7pXzJA&Dy~p$vGObExW3T>$!nFC;-VC>oQ15d<3vXz8#VLb649_V zXi{$dY6Dm`$jS>As0(hEeMVnv65fj%@x{KTL~z%T%8*x#9uGMl-&HGgt`OT3yE)n1 zb^f>4S?>PdPvjT{Zv)3a(Sr?tjD68^9ok@9XUa;{8kS}_UU#KG-$oqw-8#+XE_VP; zSL@f?)6j>b-|l!HwoYf0Y?A)4lcI``T6`8n=@AZmpwDq#zbszDFO=Lq&He#4Ojw)? z_*o7%n;%@J8{blA-yg*vjlbR~9qQsOh2u#ZT{!29ZA-00EAs#nw?6h^j!)gWgSIoy zQ;An)M)t~hJ@1E{?qdRPHzs@QGJU)FW`v4sf`8Rwzn>4ImNW7IhEWQUON&pF{xb^v zm!FfGzH8n)4!CE$3Ih7M$qWI{x6c}(%h*L+alE-^cplcZ?sofnH$E{F04!JNy|Sfc z*at$rJZc!xju^2_KkF385`M3H1c$y+@xp+-LH!Cf(IXn^3BtDFBOtIOcqzr}lF9NS ztNdJD`U~H&Y@C}Fq-UZ%zYmLa)iU<@_pdWwoh9**m&%{{busJty-b$0cqea$@;iU7 zkjDgf2wUX!b1aGV3asC(bX2dcGfHdhi0u}yH~*yam^u}NC_;L;s~@BSZWor49p%t} z>+XjykS`ziJVYMl*sgo7_cZ_?bqnAyCaJ&V`dUykW=B|=nB z=BPiD^11Qf&G0I%(9YP5jYz-6u9natuD|s{zqRc=>2${|iE!o;_?jy^nyguRG}Gpu z^Vj7XXppsXnuScv!a~T&=*S%L|9Y~ULefWx-Qym&e&A?A{4kDK7B!*<^t_AyP9Ca3 zan|xni#bOF)g?jk3fqHF6M^Uw6->pmEzd^L6pD0tk}_U$(9@wy1%J!5Mutjb?u-$V z33~=nxAG9pZS7o9$~_HDC(=T@I^sh4^DUl}$K*ewmP;=7>&khFF50NJ7ba|+fp^EV zybl6_SA&QD@|(|5UU3s>_(zZ5b+~664dO|=>d$Z&xt8YOWvKf<$ukaF2yc7nXrwWg z3Y3bYmXOdC5B&(JOo%uUb=_AS4%+wo&j-@%b2=dR+%P_PV$`vlbip6)r}B@cgagvP zwc=6nVwc8<;B5JB5eowsty&XGGNv-sXC6%+xG?~PjeeZCh4lM! zK}YIA`-JdU+&gqfaG{uv_jT_>S7a%S7cFe;8~8t=A$whlbIm)QpLf|CmfRDgp`lUi z4+#qj66>vMi0?N%U;q)gRC;y}6aqF=oM~G60(-Qz{6SC$wtxNY4;&!A`^v96cP&~&%9<+q)2nwpW2DF2B!S#zxR>n}!D-Cx$r6)M z+U34oWUlMmYD-G;{V+=n}g zV4n{bhYCkO4nJSNF>uGF;vExn40!5J0NmdD;;r>89V&5juQH-WALU@e8i#4qv>3vs zM6B~l=?aA!g@q}QHW*XtR&kqCAN5(DT&S-!A9jY#T|aIc`ugphBDIyf*XK_NxnSFf zA{lsW&l_h^y8Gm|IBEj;j!6Oga(P+03snNul1Z-t1Ckc?87e%wZF0Jh;B?8cgLdO5 zHR8j-(+uIi9TjXxncduSaU~HPjm;0Z*^alVjSnWL4%FF@wM3lg$SNIm`Kq=lBn9MzJ}!27cHwOWBA5TOhs znRa0VUcE9QpJeVHHHEY-!R8^+!Ps?29*K7c(}V8LXHDk!(il30PGP%uOH|=$2q*&Ciw?xfT(^Fhz_x9-z9!m{aA1Ik<|A>T%=kh%5dI|A zAXnJ%e5A|N{-qL-Q=DjbD1JC}fG;4q&WKYPB>MJxEdM}uyXquS&1d*{=#A&EZucAH zfi1oh&5k}MptItGgNZ?Wz6hbdubD1y=8l0PX}E7aUgaL)<+aSSF)ySC51M3jkqfKc zV9!bXxS{mO!a!+k%$>jMuGWoU zV$TFWkfQWlViQ77t2^+u48fPC!9{>`+}vpSdLp)4>&F}YQE#!Uo@JF+`BS?r68Nwf zlMMnOAwB&fIXwJA_)Yg~ZS5SCg<4eDp9r_$6O`o<1O;Iu)P``Z3h>T+hl;2$3DQyw z7<>8BXuYzM#Zz1RKMRV_l?Kh#wXFiXu5Gh>-}CR{S#mVdiVKlg5g3@@}E8+beCsCp@d zO+ijs%dJslga?cn@dy`+aqGJ=z8{K1FSbaE36eoUmY=6-cQn$U<_-OhPI=e=*Zs9% zUuya)qplT(oN`g_AI|u>*WY2V4=4pzK-Vz0VQ~NY4;T<`nq%#-zqOU&;4gi5x;_he zi#F9KuY`KZ0vn3lB7+RU+^}P`(cKdirGQ|VNrD_b_Dp0k8o@NHyc=fF`yPWI&$edj zb3V6xM9t>q>0>0!$R+LxNSm*o$1R}|553a9Ve1NDbTshf&dH*#lW^_t{BhJdRC$LX zr>Ak6Iu0BS*iF*rT`hIri;3v#rU$oG00nQy!&`Q6jiqiH!#aubMdYK%Ns2S>fTN?0 z(%a=W%dX=?}f}ZY2;$YyUePb6giH7M8V`jzhAHTR^o&t)+xb;(g^E4@H`i z%~tM*n~p&M!}&u>NvpA?v7#3|H-DXT1ds+M6IMkA=%i1*@OwgfBw%a3Ksf#nHTa9n zTn&U&h%4Nm$HhIj&M$~7JH;e0Dx&N334^i`p)^cp1hqgLU60&`Ach{BlCj7NDMnuT zIwf6HlAk>{H7_&HIl44p2j8PV=eou6?k?lFHF}*+vc*}SG{7)`9k<&Ts2TS!Eiwc0 zI{N&G+OU3SbvyjHIV5^bVL=Djzp1BPZw1pYdJ!I@r$#ml%NkRR#2?+BE3l@H-^nk( z5(v6}5wV88me*-wROIff^&@-b#%7Y$@08rf(`K6|LE|1o4f1uu6ljC zT>6crLpbNPT@D92l6^9h%HaMu@MSDVNbxu?CpA}=aD3b1V!gmG8CYz}SD+0zZp(>l zisg2AFU}$VciB61;+(_EV>F$w;h>&QntVnQy-+f0Eiuv_e#tDe4f>`qkU_5-UaHsnJ9&+~i%8Z>c1GnlDO))xp# zn_||4ERSs#IhX!d7AwOsY-yCLE~{!Ma8u-m*azHqR{b;TgXc@IlVwXzWAx)_EmMhY znCqU(Px_%bNj&fwwOFS%`k>Jj!}n1}Askn;2b+mHy7=>u{AY$whSgbL?iT5SGIjpO z?tK_~`xp5J}L)7wAs z-8(NexX-xdR;gysQCQUfJj-yeM!c1Kbz(~&v0lp=6*06W=POdpLld}oCwu7oWv$)# z7JbD_@Y5^hKY8-mq6r@+VR5#1&&tL0F74BtL`adhT=}cgrFE6Aa_)z;d5{91JF&dY zvZF>hJ4=!`3`$!7U?ub?Qk;%ZkTwVX9a4D|t)Sk`YZvts6=Ld!d_d;VdUwsnNp+zx zL2-=|;^=hd-XKoCUrbPs51)anW@{xd30RS!=IVa2GP{cNOe_(_<`Li%r;mm4NeB1e zGSqY7*XBpY?DfgWjIiUJ^iAtRT_?`!yIY7iCpfli`>QtsF@`LD;lkZP`ws`idVx$7 z^@aWWrCO4nTPcH^r@5^?Fw{DM1YEjU2*yZ^H=n#1eE~0)94a=|me}(0#>syvX!4Fh zrWg06_gD@ub96HDRe$QgTE;yL#G`b%SbxSL`-@6ePT$q;opJTeWd6N$gJAP7e#R5z zfrW7Wl&?)}=!5iP#qU;kkZTv#CdRRjW9 zn~_H<+hP2`m=*152v-@tX|-Ng#TJQNcnzvQXLZjy!9L(%cO!U2>ypEUNs*$&VZ8tj ztqBYg#!*`60-Y4|%4inw*30S%jSy>pK~~3cCV;SQ}WPwsI&Gc@oJ& z0cS?2U*(=eY9fk>-^$A&{f$b z*3+N&0X*Uv9I*-2b~6D2<5aS>-gyW0O!0#n#=dnCzr02d^9D7nR_*ttv9?D@&@+CW za!OxX@onm*{%R5cdKOcoB3_b79#B?br?V1dyk}~6fb=gq9{e_Q=7NL)%L};HO_qIX z%6sI>1fvj>vCETRpD&DmYSF(gdmB2!2584U(s@W zJ*ofqP|0OZnSdOh3JMnw)9&9gt4WtJI>2>b2NJMsS||GYiX*sG86r(u>I|^gAg(8D zp>I;%n!Ir6+z?q2P-F;#pd-KP!Z8vpp$r2qUHnli;v=e6f(}nJK_UQ{UZN&$Bo7Ts zH;oqyWEqw#7Et21qPY_Ahx0sF667onF?%7J|JzwC_ZR^tTIy;2|9SbJZ0G;lSPke_ zvR?bEKNc$ZS5tUt#Z?A+hL&Px zH#If9*0=EEm684B(#ez6=p=^2un`Af=X~EA!?^Wyv6yHqF-<>xC2pK%uV!&0+%Jk@ zw;kW2#|?%&W!@6=7-RlnBV+Rk3!?|^nXNbf=!@hT-(mUFKXSg=h-QBeJn_x_GpCxr z^60Y?-~3)Kkz{_OHj(D{gns)HLjsHgf)Ejo47^?Wh^XxvQ)fbP%5hQr{qOpAP~hlm zQ+gP%C@@6;rQ(fu5ELh%8L@@)r!*Uqnc|pie^RUqp-YJNW%!Y3DwCsbEd)Fs>oMZS zL3NX~5+X1>9>}fpJV|njl?=M>N=>jY1-G)99Sv-X85@cd!z~u&tg@3%-3 zGY;LiP&4u~F?$@?1~5{IIoC+%#YO=;_nn(mV`AnieP=^@)R&_lejMlLk_jEV6^LW@ z%vex}tsT=SeJ$(JKGisa&r$mQQ>Z7bli+*Zj$JdoxH7j(qk7^Qh2$@djzp=EeM!$! zjX|~rOhFRhHJ~;Vey!YW%f~7;B1_R$_GbLoouQ{IZ66m!T#r-Y=%{khyuGtbm~spG z`&q{>mF1^C2BM2jNxB1}r%xp-cuR)`BKi|?iVeyPpsoUZH(8_3f17RN547Rg0syXip{SSl*R1$o)KVtS2$uF5N#3bj)x@W#Qt7OpJXslmqsOKjy+x?r%YgH;xm2kcK6}QCeZTx;$p39_YgXhQSR)rO(fD# zd_QnNB#Z{EU?e#E?idB&06l#+W+1x6Ek_7-l$k7R)!3%J;6B$^3iLKPm z4$M9krP5@oLe5cV5UFy9irZ$y> zDQs+;6ZDRDt}wYjPeJ+5Qlf$_1-!dTb!WKzn0yUKIX^ zGzHsp+oGGAS&`*C(%_5XMt{B&I_za5%?y-eKV zw_`wgI~l=*4Fq@xs8oEA9^)R_%GysoqT0&;G+XtEWd2ZnGrD7`JK#r(l^6t z67HdlBZjG4!`q3)6W^0}nsgZ!^Fki3c)a#TzYBI)y1p*(c+uGH^P+Kbc>Hj917Syk zp;s?D_4dz(=YLLR=MD?6_E#@wnCM;3I|e>KFE99$-|pr%0X*#{Wm+K;{wsV0ypaYsiD5`)3=G-MGx*8>H%WlCKkmnD`ZJGjpw;NnZJ`KYync%&?I6 zn$3Qor8XUl`1=?Kb6O<`8D?ok#l?>O@G#;0>gP%v?9}u(@^=V;y%0G&=W>)Dl z6Et^f2{K^?&wlEaSh8cODe6gW*(f~Uty{mp(6li(s`GG-cey-I%Jh1!KRf@6j+H+I zxJ-IKlYs8=mxL}+V##CWYF__Iwz|>%6`I$%M#Ups#(q$ne`5a(J7IQEPC&7!?SuT{ z$vTG2_F9nc4TP`hrQ;2+fcsS$oLhsQc9=&t`*nRIlZ{7~t%u`MknP`DLa~jG`1nEn zhedJt!C(n#Fzd^-LH~rHBw%E1PmxRuih;~BRzB+yAD6P-!T)*7u9w2^*$p;l>FZN7x85C z@u1h??OPugr&-@8^MXxV9J}n^B^tErV;;e4X6OV2I;A)@Z_kgW$E~Aw<|c^$cq4W^Lx>lE3G$(cqy`& z@MAQ&WS`#LfAf98b{GQT$6Sl+M4grPOkYHw-Z{& zi{5ms_eVrnaqKO1>Y|tg&|R2aZ$;UN&S4LTULLFF%f@v;tLiJXW|imBeiYhfeZl5G z(2cS6Iaz-DwyEpOm^FYXy5J;n>a4}knYbo0*(i-88r^Ey#Rt}YMO*FOBiEmDU_$;p zof?e!Aj$wP8T_Sc(<1Dpz#1*!_smx9wSE&Sf4i}O6E)i6Qax(44E!s9?5!B2;jMi` zv#@aK>vuUEkCo$vgCY(qa~Py`O99Wq9d<}*tK9P0HJnJ_c!a-xhV{pe$n$!R-0}*$ z6%?fVjU6G^s+sBFYa0n?{IIYv;nWmWujyrbd`loRaUi5B@ziiVDC&N^pajyrj; z7!)a0`H3a|kAlN>XhxwX89D)4#vA-zlZ5z%BdikS>^6QA$ZSPv5l=kN(GkD|cskv} z4_rh9coItzsqqyrMh69+TZLP`v$z^L^`d?pMW&t*m4?KHTam+-YlJ4xCw%dh7t2W- z9Yrs}9IwK9EaN^GCe7^2Ed)7Yc*p&0>P0}DXsYEmE`bQSc>}1k%oj;lmE?D$**Oh) z_p;NIRV7w-AnhL7Q?+`J?%3)2e0e|ai+lb(v}wKzI*i*(aevd3WfPkWf^I&8y*-N( zbJPYD=ck1=$&DsvROzE53G^_Mv2fb9e$yp!6uPgBw&SyO8S#%ey*z6%hCS}L8i=j@pey6q&u)bjG} z@OrLt>#;ED(G!=Q`l&nU0}hz0(W#vv#nt#ageSPE}xc8T5-NAtKEer2qo% zPB5H}rdhyEPs#t4fb`jgwEmfVPP`bGI4a;(>toJyPrxrmK5Pb;mNlc2DJIX7Gw)d; zKZ4VJH4UeMb7OfmE_X}>6>&!n&Q&di;wy^tdgAL2%+fR5l%fLpw16hklKC670B6h) zxZTX*-@u9JudQutMbr4~g^fF&`0e<%yBq+(7He;u0&QqV@bt#ibcLJA9jm{CgEv?D zZuPMr;UVyFdC?68Jodcy^+M6GF+0EXj-QjJBqJ6W@G1yGUjDjmfzr46OgO&NjQMj}&=IiK-T&l{y+imxy7KekKc=t~M(E8*s zC%MnpwB%K6HO|Au39&XKPU3_+C~ty8<**REpk{$qSj_RmtzP5duDTZu$jZvfL;Zxq zSF9GQpvZ>AK4zT|`5l9jno7L6vAG$D0a&G`tRH8lGr6nvA2>WOb`^6JM@4**cKNg6 zVJklb`!<3g*-jA$*XC&zF$<&?3F`S@g6O);RK0=^{4bDLQEN%mLwlAX`gToom&vcq z&M`!vL5pqof!lu>h3AZ=#}kWsLM|@;Z`75d72QzfBc6zijYW{VH8i+=5d>=9UVYfI zx7AQlA(Qap2{`}Z0wBPMV3*w*#|^Z$cK@E1LDe@%IzFf1dy(&QeQFyBMf!HNbF5$T zY-)(m08`V`?@0Oe_5w9Atx7(yRIeV85{{%Ny+Bt)ElHn@thoQK@i5M2pBdebN=Aj+ zz}FIzc?aq(5n)?DeIVkdt<8`f(R`_0=qWqNsOCvHoj^dZ2x{+h-|_!rkjd{1+TRr*tQQMT@V@UMIIDKhxvRSN~qw}FBW*p3j;6UymVaa3SMWu=G`cp&Gy;zekU19IHb(u#k< z)wuHs&Zw!=#X_C!wGd(QSs_6}2&4xZe*YrB z@d*Pv?-;XmY7Je}B)n;>HvXgB<)^d2C+UGc_hobQ)OdX?w+%l$8p%O7j#hJ$>$xKOFje?v6EN}Le?b=5JM>$OIePmy_dpQdJ<3Mj zbqryV93U%>W|M_Wt0yCG^2hdBBQJtOgj8O2%_DXY`I>TQv~nhTxca6}un!;oZ+N^q z@&I?e+d=6m*F!@G?c97MUO0HLW2TeG^uswx@w2c|;P1W8g`Ip74@A4{kYl{X5w2K4 zIp%PzP)g|EjP(&g`Bo`GL48jiS5t}o46y*Z`;+@XfFED)@^J1&Ume%eK4<)G^#vJC zp^$p~!H`aH1>T=|&nIy*Y5iNoH_995*A710S2j1B91Zyp-JQ%}rzEh8GCRXtK5!Ll z7mZ3oe1eMi+4xW#gxCI@om~>Qhs8`&#xthua;JWxtf+3rK_{TOp)u;VSAmvl04?1w z__$54uy)Gb26s3!)*fo9qodPJb114%F8wOP*+7GQVLMN1CaZUkxyT-B0Pn%?8ys7J zU%L14rq7BN5D)#U^Im#UIbtETZ=q-vs|flTcu35aYAUoUaaUJ5qvgD5g%%}5h9kX) zhbA8`Z*U{b%|~lVZqM%{lC=3Ab^{OfzAFe~(Auag&zjGsRNrXprLpSxn@Le-OJ-yx)i=hu;a3d{!~^xxi1}Pi-&lHApWW@p2R4a? zh{&cql21E znbrna02scV&woabSEFhA~agN7wB@hg|QmUqsMWI<-{Y4=40g zLrLWi+syfWoWd)VwE+k=4RKV;tdh+-?2k$2`C>JnWt`4v#5V>%nOf^rJimjCj&@R>uF4Z|IPR16$1FCWyIT_oVSWF>u4A6=S@ZJcYl*u*G(jvu ziyQx?B^tYmK!aV!GpuQ!ns=Oa|GVDJ5Uqknx=i-hmT-L3AhJwUZGQoLY8JDrusbSi zO&@V#W1k4x`wtbc(nRP zj$#0aN}4RZ!6^GwNrnSxO=cxDq&VM)V%1z$2m^fgD#L#~U0-1Js1h~QZI86PGv|gP zH6}YbSi`Obv8wyO&l@>>pM*uD)EBm;fxGT?uDTLC0TtR5W~L+Y?RA41cQ=?P&TD!PHK9m;?(OXdsPa%KK4*RP!K2FO~9Q%1cT4jjP`|exfb?@mhus+ zJy{5C3u5-v0L-nUwnlW%Lbx{({VL6c0aK1PZHZxY`q^&6b-#{JVa6}bh0Smnz^t3kwBai@)AXK5Z(u}r#b6rnU zsHsABVjAtBrK<_x^Ij-Ze^YyG7AEj-X>3EU8q!PJ!Kd-w^Ss>ra;BZAH2!hGDE==Y z%7!_2_wDiccd&t$-*uhSxSe>O%Ydx@R_4u>w*KaDPWgzb8dX+n6Uv<6Leu_V41In5 zKy+YN$Wb|Z!B$(HgfKDRTs1p}M%qyvxIN9wXK-M zd;zyDtO^PWofH4&=DP!?Yt4tK%X8jD;c$17xvp{>KQKyM4J0G6XPU_pXwCXWX&FP4 z6Tx|a-D?PDrXSvx+pU;x^o?@=KccPzD$3_;@6z4fOQSSMcZt#lf`pWWbW1PYrAQ+U zBAt=~i%26SjdTeTQqte7{{H7X#}!=Pns;tI_qliOw9|$0zj;HjOM+;awa>Q7N$W7e znuVWf>g#XULk@x+7eh~zJ;d%=JsR-A3=Z%Qqbmf>fMhLd@*<*?qUD1z7EmDL>{_A0 z#|0@G86q$fw1cst6sPm`l(P?3px>+5yc-pi1wNWT9HpP!CX^~{^XRG}M_?LMmTcPW zE`Hiyj01MjGp*^MIdp2-X?(e0cxuhzXqGoh*YW#ZNh%Ca0oUkrU!_(`FxDXuoj6xc zMbkR|amn86{_J;2#EPlCiuIK^P>J2F&MSe<%v#Zz zofq3_B~PE|irvi3@AbMi1yS<9KjNa1LIY1%8L+woDX7ynGCFF=+>73|J^VdTd9^!T zZ4L^piZn9Jl5ZzS1F9rY`Irmv8}eC0NTcf^yqNHX7SH^Gfj(9aYXG~k3Gt4=*Z z^nKV!{b8VUtU+`m*gSSD-o2iEAzAe2iylhZBXr*b|K8q%>#LwETXv1iKk|)D`B~)# zwo_m+3$I}#QK@eE!)AQH!iow4QjUjNTRvVlmnB|PT&#obt9xik4WQlMd?C_~gS ztY>xmv1v3VA3?jZvFiQu<^w;g;oo8*rfb9_UB?q~g}X1BT-Ydho=+5?o`lxhPOU`U zE%5H9Nz52GF30=TJNc8xIr{rwkBJ^^SfB%iV4^;GY;;6Sl8Rdmqg&IAxThdCJjMF6;d= zpI|x_jVn=0C$$>b{BT})D=^2Tcq^2{4^vsr0mwxB@;EE+6rrJ^;kTXS78Vws7M+s! zATYKKUeEkmQPI)n437JFckbSmVMk+> ze|EoTl3r5$`P`Fmrz;`GBJ$zi>jTvmeQ)Y?Dau``Kr7;DKzF$_M@^V)llnJZrO1zp zkxP9#6*mmHK3UG{3MVIDukAXkmq3s22_DK2EiS{aKJab>L1WeTijAnK-&`A7DDe0| z^K^diwIkJpYe>|%02Nk`OpOS3J^x$HSK^R$Vx5ZP4|`nl&$AG3*wm`P>YTcU83ZpN z&l*0pw%W-+s?zwf;Fb@68xI~mvHpe91PSBkEG=qo20J;vs8TyahRi5i%y~tR0mtrr z=Q}L(HFs#{rpEaPe&a!_uLe5CA~Ev%1v(H{x;_vlQPDFFN@KBeXAA)-6vLDF!#ky%w)iY4#ED%}d@e~vN$a=Q z`{;V!WmlRII{rKpObLS+OJr1UeOm_0Z+EH5w57P;O<`ZZtb9MmB=C+@Qz zOLEFHu?N8!a0DE%!6_Q4cUg0k9Wj)N|O3HI0xtXvN}KL z45FyNHlRWa} zKr*^y@b2#Ji4r@y@|iW=!(_TC6vd7%NHNzc()nvpkH0A)zp~7 z2QORQZDSdYe*Ic=6Ta_Ft*800VVIRoFKck{_gHN`fK9L%ODLV`277`BSq?>NR##og zV@4`bhxt%QmD~1Mk{h18yE~H5h`<8IjVW*AaP_N3#Rtwx;T?PQKb*Bt9U;E&8t zP;mgzqwQqt6SJWQU`6zMNq5A#PB9s>657zH_WRZ=hvc)MbF9=1DzUG>pSz)8CA~}b zKLK5#Ghe>TilW?_t+$m|f@Qh9eAzt%8b@c}|IAcOwxQvfv$6&? zeer8c-7N##7dOtJ_qRttRN*=~`8h`J_4?0rhS*ezWnUL^G9uvxc|1yDm(=B1fMJBf zOx4xi4d>_PcG6%^4s+klep+Au)$w{GO(qo4hAA9t5+=7j%-!I+Sy4JWJG)oUNV+dXvTeN8 zd}C%~vz;P$FkmB*_5i;C{gEFZcaf%8wqd9PCG?!hf^_L>@s@ya_LJkHsISb6CvUal zdr*PJ0qdl`zNDWV8G+9^wD2gUJk!>88V80l8*{oRhJgK#A;Ep7^dQ4+B26Y_)s=|j zv{O9MO3U?k42^MrA^Wc}m2HLwbEO~r9;|my4AVCUyI}^`0{i($#>P;*R**vttr?^q zDGs$9)>}?6?am`QB~)E~k4M9cKZ9D@?}Jh@ksmW$U0vG+&JnoT=+O{R%}&My&msuN zs;iIq_{^iAaO(ZNxz*X$l8==eAF*|`wfXl}N!5Nt31da1=rx5e`fX;7f<1;y)6UH! zlhu@Nom&>7W&Ll?TgrThO$by-W8c_{7rq7=6IpThg>@$^7#$ijGfG5cnj75c#QkI) z?BJMv4{x>Bue>rR$3(vGOXO1;B9ov+A{C|-ml(=Y(XUckz#^J#HTzQ(m{rO;(0v2J zxwy`el-etzGMmcJziT+i@e|{D=-YgUm0L&-dFx#y+|GTGD<-2{2saK%F5b89s?y{Rg2A^G(yxW z7p1~t9R$T{$H|SHI>;<92W+M7wb$IhGpaD1zClW*M#@S_qv3$TR1rVdfW>I*&3E5< zv*MJEO{dR86~2rJ8wH$ye`bk(6^H{lB$h)m5;U+e@Z z{sMUto&$d1kbZ}Oze7>rKZ(E(1=BnUv`~dyZPirql{{_EN16i7Z?E*SyRWAikY9rf zP>+7Sk+y)cv2v18o5~cJ5ZUzkj{WaH*&eEDb7Bw-5F5!=@xAwdmHqdesJCdy!%>5W zBDCbHk{2NJ*Q471T}}ph#s#HlvmfCb{bbALPVf-mKAOHs2|3!wj10%TXk{_x+4+!% zg0n9Fog@7B9FS(#dd$ywczLI+&*YN)?|GRWoG|&^Ua3QC%(6nXF;*^O(gI~_3BvzQ z#DXw(kBskiR5R6)La}_SOm!+`K>I z`N=+eU7$aP-S@x4(R36PE+W=cn=xE0XHeIlcr&kt*{dw3Wem_<=HR>XE^A70CIArEJGE+S%> zZ&Ceac5m<-0b>W>eqqJEh#Nby@L#lfUR0h(g^BN*-h9z|_IFmgBOkUZpeU1KMR0a$ z$W%<@yB^%zXr7^RUDypFaQNX#G;~Rjd%lP{TC>lT{`2h2Uz^hy@)YuO=$nLV2J;Db zINdQSgq^R2MseV)8CY_fV|pTT=^s4mM|j#rHxl3EAVOd_9UG^3zx+Bm*UF7WvKxzB zY*!dUMc!bKZ5I@N!u|R33;U&vJH%36n&qFA9e*js(^gP$;YN27g@_&1I)lNFJ?cxm zxQ3x9AAJsvq|mYAJ=w5&dS}j)q>5T(yh>kdAuu!7fo{5rw(@p``qIC4#Ls?H@f9ir zl2X=&%QxK4G5dKG3x_>4KfG&k(uPt{7ph0s5P)q*FbuvjpS>1c`aZ{q8(`^xT9ud5 z{Qu$(YioqrBcPD+{dTA#dx+M7B}Ra)l{W2Y>poN5#C~{tz0j!HN1{9G6NNRgJuru9 zLE23`+L+(rj)6k5wD*6Wt-hOMtle*+a#?a9CgWBa&I(C%We?kD{KhzdKX-(D*URaF z2NFC%G`v>+BK?d_}@OjwK9RQyGU3g&?BJgbLPsOCs_Iu)%td?o{eHG_F!tSTF!`kF|7WZU}KY)7M= zU`n{#{H6eDFhW<*fVQW>EQp@9=G0B0A_b|9P?~X^wKrXyLEOsh<80a^ghpoi8GX&% zV2>I(Msnn1Z6UN{Vc4Aac(6xc2~?UsVxcMxOP^r4^2bf8k?nZKxQm#Y3F&j*h1Y~4 zFI?NNpI0io<#yp`^d*7mdM`T%r4YfZC0Ly?mx2?E@!Hny@FH_AmZzGn@CwCBUVe^hzOq}DWIGxa-t^lbrMHko0>F~&i@<@G@ z3>D=3e_in9f=3`qDIcXqK7Iesy{wY4v0B6>Sc_2l+RDe%zoFyCa8wUa(W2oj=>1Yc zu#k=|YKOY1dRCF5UF<>8nY{dGEcOu-9w|S~$hZ=haga`QnMc^V%>i?bN0+969g2OU z99d_z7Z1r%_TOn%aHh)%2^p-$BF2dDU>=-2we%2VY?xX7ecwTTd289vcKv}uW8`C? z_-$b!ZA-&wXYOLL#Vkz^p>!ghD)|`;*D)6|LP|6f88D#8y#|I*Yu` z+=d0`KH&}wuFOwp5W2OjARR%n{xM#2U>VI1x2*R8@oW>y*!^75WW;ec#9LxPbaJmd z5Wi(*sA)NLLc&f!w#|8>^6${{vVk45#J(WXLv2+3V<5@$vNJZ~-MurVQ)Q|TA<9G_ z6?UmFa+>sCe~uv&zS73X->=>6l*04K4#Il=@%4@i%fr+OrNd7idCqgjbONDR zY;u47qX0i}j~kKJDHUS=5keCsSqy#S+=$HgZ4vMPa1+m0t(#S4G=yS{x+f6FuduPI ze=IDF2uG#$SY6-{IdNkCMlC|cvZ?+hb;lADefg`s&*+nO+4L?i)Hj~Hx_@s#%mE}^ zO3g%Nz;ny&d(*Es`B7Nlx)5x^dc=ULf5jAyYz9!vze~M zqghzoxG6d};EsfEX*BULHzRSUBqWw9gThqmb}bhFJzdUzQJc4rw8OiItX=T!gQWBWe12b);OtFi^!PvT*D0SBKv zkS4Uj@p z{^{SdbyrHG;X-;AfgB)HKfUHJ#`Ib~$Zk1tUN|_$OZS7SdPG33Le8E%j(8dxOKm*@kYcFcB5q6UA0b1Mcz4qwI!g&;5gH#jb5UY zN~l_F<`a?+Ai;|2wSS9T9c|mDt&_nE7oP_|Q-mfKY?C)sIeCKq8e&w&!Uw8^@|YV{ z)V9j9N2XiXdSPEMO5}MIfR!W;m2Gi^KTt$j=TOwn7ed)zb+-~J76x;uajZkvamtSc63h!N|V`2p<>7h9g) zE`~aR$7tO*A z4GrEAR2Zx(rnv9@_4XT@vFdn93v@e`p7%>krV#Pat&BC{KnryP-T-^|sX17BTw#2d zmrhzLRjX65y&oX#t#3pQZwl+M{$8Js5g-@8i8M8ACM)fC2~P9CuRz_nVY$*19Lv|j z^&BK>Fm!6qp4@z3Xv*(P_5kk}mx^+?`I^*n#zP-n+{rWFV4T0x@RK91S^S5Iq}Qx` z>+%0a4p9apUw2MLEe-oV#}8oK^&E|I4O%@G%cf>jTBO(@M=r0QMpUn0Y7_RU)Ukq+ zsU;F+cAuZ=l`^;n_y`K|HRrRRkc&2>N0YevCD`T2KV6cRDO#2D*#(AAwANR9$u z0HF1$V>DUhBNT((+ZA-7ygf0<%I(xiLomQBZ&Qq7vpi*A+CLB4iW~GR#92s>`Y8PU zA97^IM?$xot5DwC>o3sciVPEQ$j)O2Fz>A>|@#$wNuuF3&=;Zk_4 z^f(V_46*gEnEGr%jz>sgSrJ1HcN3bbpz!$GA9ynJ^ioa?G6zU>**m?@SaKiyWX7@? zVI-twe0>{Yz0^PBQk5m%hrj{I115=ZRkF zLwiqgC!E0snL`ERHKB7CZ_3_Y0lY6(Whx4y4$ONv!&i2AY5X|(6iHvnC=iH@$=m=m zwtsezOAesKW#`moE<(HM@{o|#@RoAEe6v5ydLcF>A0I7d8XxMtAR#9)Zo97jFdE4n zK%<7m3BcU=UVCo13ilw#s{UUE&J}ae>6;Mjy0fDOBqEW{kQ_Ch?eyIb^;it}xC_={ zdY!wl_aUO&a|S&G140Vbo))AkGIs;GVKnr6au`mBC1_*(LqdO z)SwQLM3;=v*9zQnzC&yO^b3y2bozKdHU>r%$n??3(dqa41nkM5%)38B$ryC?Xn*b- z(O(^Q;kO{p%A-o5|1OEa+CR_fDg|+6@-(^TH9ROszn)89r2@YGxm}U;FH>SKME;)e zz9d$F9@%^2gN|geGEp8BCaX33*DU+*bm#l%$SATtz~U5tmh+w3GHk?G-AQZsH*zX5 zASbS6^_5Ef5k9TN4i09Y`u6RaWIM1gnDKaMX;Zc}r<+dhZ+#=#Q7bzrUXDqX?5jZ6 zN7;62V^*QRTTXs?G*@s%{MYP?0K0!r~~E+h6UUVFKlMQwYF_LZCRo> zQalWEH&~hj?j`n*3>(Y&5`WWuK|HMRDDamBZ`s~PIm&c^7^-TuB``I#2b&jhz$GS` zWPj515BowRkzkR2jk^>R0^*`rXVD%$Rv?J|ysQ(@Mto_^Qw~~(kRcaE0;lM%1`W9n z3;qSG3b>Jvqlt>L4)X%gIO0#(h+SDE6+tWnApiRVXUxfPF`m1RS^f&ICNkRhdLJsD zV+)`T2#bXGP@}=2EeoY=Te7tTT1op5ek^IsMK;y3>W20R4zG0O)W>c{60<_ukvYox z-wB8QGa6>d3+c+u`l2SB&L0w}uuP}>jK~HdHr&W)WKj2dzwMdEy#rv-rEAb;1^+CK z7lF}#y~9D^J+CWjh|+M{~^x^S4&1K4CMy|GIPm92va)>M_yvtEVpr{=q>3Cm=+(+UVk` z+!zI`R;{&%ro#i(f*8eby&*?|4ChdYYUlFF3n|ijByWKc1dv7x3tuUO)gJ$PBxcDK z_&-KIL*Hk21}cKdaCms+pH3J5n^00BHr8Xt_%8KXcayHYM$AEQ-S@(EcNu<`@oAWP zQGKjv;v4H|>X-V8n)79GHqtYDIt2O2eQ7QL0M539<1AK&SLM%Q%P)DI4Xum~KZtq` z9aMXc%v|-Y{VN0l3@}V=aAr&X&Waf%Hzef799u+qC&=$u5F03@oAVNz-lYN z7fg$mqdO@ltu8=LQJs)+MCm8lFq^Z*gcWwnFz8Yp9;DX$ylvX>;o!G{QRzRPAocxG zhT&guVFt+qSMS3084kg3StTXG2s?6l=eMqUed9EqC!s`11o7GN9+J~Ugu4Q2sHSwcb#k~PwZeOpT_Uwj#FZ}5LPZ&@!!OP+! zWJ=Yur{w)J8PrHXBR4$YAeTNLqWm3{y!;`#e29kM2j>VH_lU=LUXsUA><=T{Ew8Fc zM5_WE1dZ)kNNPBF90P;UtS(w^@7$U0-M==1%9<0F8=#LnXVPMN($H9QML@KJ319Lqxb14Y^Y>Q0kcX;U0Sxf%0AmYYykVk0Ck7mW*1jZLA3&vF0X*Ezm_*Tw(YA+U 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%0A?f%D5$*>5Hx|=IUwG%DfaB`O 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 J!y~c}^Z!KxRto?C diff --git a/images/Logo.png b/images/Logo.png deleted file mode 100644 index df5b12d632297bb6da6ebfe6c1133d176041c85a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53648 zcmbTe1ys~u|1LTPf+A8P0ul~LNOyza&?VhD4BaV18K@xL4I&)_(hZ78N_R^RAt2pw z_xQfQ_x{hh|8wqH$F;^avA?_bC!Xio1ies}!Mj6#2LgfM$;rahAdnjY5XiNCoLk_3 zqWvK$@WX8c7Td@ARK)9bErCPhiH_Uz$3xbFy(@#`L$KIs8B6oZMaP z{x)t7XGho}>=6!buAnUEf66*pIl4KzS~>nNs{YT<|3d>{Zk3e&)5d?>7km5vG~wzd zAyUJfSI}>M4@2axL7$jS-JQ%IJty4c!hYl_5M{#$;Gm3{?A76uWCK55TMfketrK{!qw5j&C}EcAz=w7>i=N#u>Y@m?rQ4s zzwh_|*~*B{|Bu@Fdim>f((<7LY43tt~%bS=ZB@sTV&--^WhSC8*U zILm*;m3sd^g-VAYH!H<)(xiMpWnsU9Q!cu^xx!G955NBQeRZqmCu_ILl}=}Ri0L

    W}{2rbSSFx4JR*Y3Z;) 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 diff --git a/images/koios.png b/images/koios.png new file mode 100644 index 0000000000000000000000000000000000000000..6639703a9baae5ae683f13fdd41a205e1ca51cbd GIT binary patch literal 15588 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 MR9PERtq2SKKdcaw0RR91 literal 0 HcmV?d00001 diff --git a/images/adabox-logo.webp b/images/projects/adabox-logo.webp similarity index 100% rename from images/adabox-logo.webp rename to images/projects/adabox-logo.webp diff --git a/images/projects/cnft_dark.svg b/images/projects/cnft_dark.svg deleted file mode 100644 index cdbd2ff6..00000000 --- a/images/projects/cnft_dark.svg +++ /dev/null @@ -1,410 +0,0 @@ -<<<<<<< HEAD - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -======= - ->>>>>>> main diff --git a/images/projects/cnft_dark.webp b/images/projects/cnft_dark.webp deleted file mode 100644 index 0d2ec1c7c1c00cf6e17afcb7100a9c70533478c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53506 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 diff --git a/images/projects/cnft_light.svg b/images/projects/cnft_light.svg deleted file mode 100644 index 0b3f4ca1..00000000 --- a/images/projects/cnft_light.svg +++ /dev/null @@ -1,407 +0,0 @@ -<<<<<<< HEAD - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -======= - ->>>>>>> main diff --git a/images/projects/cnft_light.webp b/images/projects/cnft_light.webp deleted file mode 100644 index 1dce29a42cf5a3e3a8c9b51db986fdfc27ac5409..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57328 zcmeFYRcs}})-`5kW@cuOnSIR6%*;$JZY?+v5D=7qM+Ou~78D3TPDzT53j_p| zV#FSw2ko8N=h?+qVhG~ptXWsHX}GcilN{5Ils>$fm;OldFz~Y1l(xC%`$B5b3<3fH z0-cO*Jo{c{{zye{GFurNCzUBJ{r*Q;^E6K?gGnuH&)@t|@0qYrc(9i}p_A^34JF~ENcV>;l(LSxbn04 z&*Q~h0IiBc-a^t~59h*FkIKC1;1O5_vOkfswI@C$O#`+K!tkV0?1AlL=(an}XB3E-}rUMBhB>K zwbm0nr;ziu)Yw%pj;2ka2dhF}+Xa7;`)jA71v4|Y%2NbQb8#}Ao)s=P z@DfXxZG}Q&9&+%nH zq$IR)fk5GC&=0Pr3vLpZ03n83kvQ;KqqX*1;bQ}TsZ+bvLE#E)4S!7%w@eu6uI<4- zKb!`4oOtgH80br70XVbj`091x3wYAVGH;9*zOeJ2v;wNe9|TR%&lGb+vE`TGhe&RN zx0cT&PB>wc)S&&NkxF&lZ6|>od)&|9p#?xM!VB(bn+PceSfac^v`}ZzNO}YiY*qGpYgp7g7~D0( z_Y9R^938l)#g`s2Np}Z;0Z0BcE5pKH9V}Y?z3;4QE8#us#%sFRZwCdQ$Y7s-VlXqX zkY6*yN;NeUAT^{B0a!1#aN-~MW-ksfFV4oj0xW7h;TQ_AMFn*``%W4_jQ2>Cu6+^z ztSF_mNEZ++pa2=_8HdwuUyvvi5&?JJuEgtJtM7W>l<(F+0Fg3La4XAF3!1cAxY1?m z)WfzzuPRgbQN=UX=fd2C-UGvA3*(;_2C=3N&T4)B@0x(mgQ+5qKb6&W!^r`iL|>Z5 z>DL6>X!R=081KX+u1AljRRKp}cs+V!KQ&SNg0_Uf2W&8P7v(^-BUiA^WKi2K zXE(RIf|s~p21O*$T3O*=KY5`J`tEC0-mEGPnY>aK(w-47 z_tV_&_T`=KMLxfZ8)L5evQJ?u}l6!F_31FdFnE* zNODgw%00Pf{sVBy#yCWO2r4U~dd1;2Wxh&-wMGk@cuE`XV>zb6%*wmZ$Z$J@7)3knZ$}H!1uUT>ZJc9^4GF&aq{8##%{K z+hKPvtMpqL~DCHlebwRXD#Wl_YA`2Odfa}k97=Y@tVv&4E!k6^RQVi&N$BSjiQ z4CWp!jIq{mmLQp-u~)Dn?GBRuSLZy!9eHTPrhkCWUq^ZmQRm;#kxxhmMHB_A(uP@7 zGV8mu3hMmqOxF_koDzmO>*3U&sramFu5206Fb6S*Mx$j~L`RqS=ggO7ZeuNfIT!!L zfs#RW-KN&}(&5Y8@Q=CihrQ^DDDnES{#3pzeLZ}l!)BOqOKZn7Jw6Q(Ck!IZD(DA7;DTeLl)c752vlFUK_%61a|2k%zaEtO{@Q465 zbA~hulBboBu1VT%$6luk(v{5%R;i80=lo$TO$wZQ^&(mQjZ#gk!{xyAtR%fYqWVh> zA%?M||03w&gzhjAR>TR~dh-TbjyPqxTu^0r^L5MUc;RFl$z@9+#3-ocxjUGzylG@V z@+|aX$$^=Y)9?5=u^m!WugX^Z#6n(8-&l}Ms-1gj}Kd%j6P<48Hw z{-Aca^2mQ<%N*6uS92WWofJaTYQ1i?O<0_>(nFQOY_YRd4eEgrYW#{^qz-AhZpzCG zdRhA>+`9_%ILyPg9b(*abnF-tVVWnE&yFI~p>N(>LAiyoI+2$cCIp*#b+ zm=c^qpHu^LCL=*Q-hg7cf@;l<+NN<(R9qkbyl4z42xD&3W}CyG2+KJ&n-V$kg!B65 z)X7$T_>Q>rLAoqcSaW}iT$w7nW)(E4?JobwAE{@M6xyomci_6evJ4I4sXwuZE}zV% z&?i0GJtre1A&}{;bp16?{se>=6IUx1tvIO#^tjNgi+D)2CJ!o4WcT_@g;i7Hl&Y7? zhadYc0hoWL)9sB2ZYdv6!YbE2`Lq^QD56YM<&S6B@|=R7 zuVg~(Y`Bu}l~KVB0+yY~jzb$@+&rNq36H{Q6Uxt*yNi`EQRX={r%pwKP{Tf{2|L0H za_@S}QSSb9rq~7Bj9)&Nr3~4}!;l+R3T1?IpV4Jih>TIYQ+w&KqCj4IyXAT2M3er4 z7WRoUv@EZ+Yk`YwF4>ZDnFxvqT6elX#py;1V^i??b;7cw?U4gk;B5Mw;HRvtZ`;3D zUjb{1c`J+me?22#@0`W1`1t-c0Oy09>G05uUK$w)E)U1;5?@< z#O_^Y8nF{d5Y@7I{eYXM?{ze#8k?;}ni7t4R__(^1~D1c5&QtJ2Hkzulh8>X4NbxF z^l*ajJL(ZEoPW)ey7M*6q56x$8Cq1|LNytBy<`jU6?As)n;5x4PwHu@%(a&{9UI;& zP&bVn%b3#liqNcW)~ZT$XxbT6tx96XR`lc!vvO6eK4s+u;ovgrX~cI{>b!JnG)QuE z+dJv@juR;}B_Uzw4)cT+g4mJdgY(-d7hhz<0ik(ATdk7UN^fquXy^ATETxNK#99YR zSC3iB=N1BsZ$C1%-K4^j-BD|}^tm3a4Oh-$=7LR1 zy|1K|!57sI7~*`>o)^&2Tz@wl35WYjt?Uu{3#?a^P=_B>14$pI-pLLf&*wyr>QkE+CsE5V~cZ^;?AYbW(Xk4a7+ zj)N8sjf{HnHc~%NLS_|zbb${D2Q|x*r=zljb3EbBXa1_V5PbA`Wr~dodIg^-iYDs4 zB=P}Hw*1r>zfHGu#+lJc{uQKn86!N)%FfSnDfMCnSkRp^b07QzFAhfWD7|h`L4f-O z&lYTp*XZz?r4Yi>L6wv|8mXD|-^FOw?v&JXgeBiw*@}GKotZQ{N&5In|z8Sta~^|y`|8yTbm z%+=qJiz8#XZc`dsZS+&(JLgK%?sWDWY)h%H8+jIYy#DCqiyAhV)M@8tsq;*_5+gow zTTvM9xY&jmoUDT6O*n>Z5^uM4NInTVVCaL9+4*DrX8fgzltLl zB%I5_r?CX+fCK*J{?s1p9)sshsXIma36u}Tp&5iDb~-xwXj>;*QgQlVGmv{x^-*uS zZmv^x;)Q?_OPrI<6+@nX_3BCg>g8pVwzK9o#mnQKjHs zuPTG_=@&iy4B+Bl4A0d2+Id}eLP(DnoN^+fJi%c6{v^cZx$9tWyKY1Vb!GY=YmJ5%jOkrQ#pb% ztJ(~#raA=qbQn#~v8zhpgO9UNxDM8Z2$1hrHwZ)BtB~?x1o6nKgomxAd$E&6(ZR21 z^fALzPIcx=*)kdtPC$C$U(9?zjG~?)(4rLVjaBN1Rf*WCOSj*Ae>E7;RPEA6nAf8# z)U`24C_>>}>5>dPQ!O}Fe}c!apVJK{f@JE_#Ff*@x91$?cdg1ytg#W@cygb1=SEJy z5`~>3qzqCEdd0Ff!q5}}j2JYGX>|Fm^@>8zo* zY`OzYksn#ZFb2qo^BrB>A^~PNJ?oN4*DuNSUY|iv;CH~LkK40gJBEVpu z!|jR5X>*}@3?y*A$)#M3Qbe)u1mQwPu&nD7)0D+8d9`t%M-Yi(pvX3Io{6-2q(^p^ zc44BeBprFoGUM(1xusmkywjKtd@)^4%7(&fB{?qdDCU(hI^|T=P(m+#{RUwVWWII( zu8IEs7;uA-SlNL;^pwhF@TZV+8wv|#(EBHJ6y$vnzwRr_a=nWpD|OP+fC0oq0ct22 z-=}MWwqLKE=C74|ccK7HA=qMGURmrNKqw0r!x7{l(1`>@PUvDE z*dn+;QED*j56PN4$Tvc7+J^E@M1C_uRx~=y;EvewW)|cNc0G9s$o#Vv6U4=0>G+vv zc|Ryg!6B1X;Dx}|r7fTCtrB_zA6 zLI+f(_7qWlCrYV%%DmD|n)tieY??!JnUIUNhm|kw&5#_gyHkUYK}%o5m6+4Pw#^u?tr7C+=2KdmBKC%#$E>p%&u zr*cBIriu=AXWL({HJQDoq$eh&j#|YuPXdZJS`~t7h6F6Te(M3>dOT7-vK06FJ|(_J zxFAFz%Yl@N(Jxu`8|HW`;w*|Daw%WVBHEwvWXA7-7%N9O-kMdsyD9+9-yv8ygVu0N zpc!A3im06gW(GR4syE4W+Ows#U$M#;cB)X>iW)aKN_=%eWY;+HXOdD*bxl$>bwbpz zy6Uj&1=dwntcCi7mGO?nzvL;Qe+d;)cY;ebw~8n5fKq(NN}~NSNO3aFb6XZ->Cl%$ zyphZ+m&o_@5RTH~2RZbXizT@yjsI++l3v+K9`~hCcgK|Im{AA&2%+H&C0nDEj1=Mn zPxLcDSz6chzG;v}Wm2IsdoL|#8Y-`aRSV>rqiEn$f+%r>r5hT_5pKy)C|>2pKkQAB zPSmfEThmI&TG8PQMWA*e>7NhEBP(5I7Oc2KzrEY=^)V}Z#& z>ko(DH=Q7Ne^iw^p>R$pe;)!V#R^_A)IofHquipbPfh|Bpe_Ga%zOw2`nsH1mxtau z7}9f9KShZ6Lu!NYF~Ss5`}3e3`h`ozI+J}UkgTMZW>5TKxiZO!$73pnd%bl7Roh%4 zEu_ePZiUXM6>;Ih`Zfq0iJ>jaOXzD3TXNB^jF+cC0BP}AN0~=?f`-NlTB6r;nOeW; z1Z}vjD2pky|c*UjO`DnZog%>~2^^(8EKNGa-RdmwJWZ55Adk$Qd zZk&6$NQko}G9w?Dup4ZO;d7pwt~v8Eb)*6zShrg<9_EJvN%a0o!6A0U&n|Q`Cup6t zP{80aTIt2}(oK_&t9wavpojS;9=j_inDo~kqmt`>u^dXC zZz3qjT@8hlcWKN4N5_7C>3bDauBxhv*z)C+4g(^+}gPh^?8dU-)D$;|JB-!U8!dqwCJ zo*|3Cix;&yP7$b=M$$Z{g?Po)rr^$qanZyjA8^(+9vZ|ZtfQA;azb{g!3z=rzHrog z^Fjz8XOjya+I)KDRCutzA0k=rnYmk7b7-5n4E;&jSZ1T8{$bt=eX}Lxz6(dQ@(WcqshdHSTgPGM5*YUg~4xA z#39ckX>@+a1>i-Unqmbhrrl{W!tXM~btlAwA$Gw2Qf&&3-QTQZFA&Pkr4%_1=k2UF z^(*vy`8x<`Hk0Zy6@F3Q^Cj;*wXzch^@uM`jVd|0-m&a(8ItY< zhpl&P>Tdxw+H})-pzpQY%sS19VhJ#s_grS0H*e7-TA>um4Tb#7r^I;zLArgiL`9Mi z5B$-lMKVe^L$04)0{*UZk$bhM`n*k!|1P`p;Y^`;IVc2fyWR>QNa?3KQWBas4q<;b zM^LFAhq{=lg*byoUOB14)tcY+S@L6DIQN~o#yd3qYzXda%nIOdyHT1(%UbP1F)$|T zTC6ZlX`$@`dCk|9&%ST3E@hzBrkfWiC~m!p|OV zyYzEtO1^H_OalI&G~{?LJk*5oVxK<|C|8z^^dcPxA`|M&ZL*51T2CVNCWoUOk`k9I z(ntI&vcaz?_fPT(xRT5CjweFtJn(RxF4CENrC9ajMq{xvDNbV?9fsc@&WjNr(>Gfq zvOZJj-qef2H`p)yC@TWP!!Ma913gExe+x|ypwc5pFaZu{B+KQ%?TT3E=ObrDxmEm1 z&q8D34xb0wY4BPwtBC|OvopZf6(wcpCNgQAE&%Q1=#Kd~`mRgnDL_9Y()_88XNGKx z0nx2ANx*d-8lVoc{1QxE{Y!Bv&TRS6S&k}Mu@6HUcPeY|%_f1e@= zkEdH~;O#R(k}3tJ51YSk7?+B2Z*~V+#Las3u{3F@Oc;QYc$ysKUO?n0Ib{orqc>#* z@UYn^(IFM^+?aGZB;-_~n0N-M?AqHtA8GI7utIIL8w zv`EKA5$P4?xPsS>nzh{wp@l8dd0VEQK^;MlF1!@xr36X;a&aG<(v&4@9ysD$k+Rog z!@jO!HD-jooDa{#xDp>yZB?NN?|~#&mHoH&Tm}9^1@4J%kOA?{H8yqgB?Q5PC9>W? zy2>{q;#J3xQbPvxJ2G&R<8+Qmrvpi0V%PiM+#p||&GLF7K9_e#;ZS+u4Castpt7_! zwxqV8tOD1m^An~!^8L6z=Tig4mf9;qxU&1iwVy-#U}Ft-&c-=kSDOGD2CXerz)b%} z*q#lndMIm^gzrhp?q`g~It(vWl7Q%it1l3RbxJ@GL^QT95HLb*`~jf@z!vwTBl$WjXvHV};e#KVtQbccPtq6>|&-CC4m=?&Mo z{Ne1B;(jrOXs>{Y!*=;lEywX``;_-alx{&YBNu)C!8pqmJG34u|b)xM6 zpkBiyEdTl$V>_i|agv;>)3O2Td;g!?@`(RnrWnGaV%(J4sinPv-U?DMsg^}=x#I;y zY3XL-{k0DdGw5#eFTO6rQE=SS7D4b&uItau#$tDniy;V8uD+vOK2ChrMexYK;+IFH zms#6YR)BBFSs?o<0oJJRGLUj8Zvw&fcZMM;{ewwb_nOLNsM4h5;Cf5yqY2_D+$z63 zVG;YVc@pEL`-&i&a-@UbI{1@^am=D>3t?{0Vd+m4jEOu+5$40R=~_u9+a?86ugvDI zbrRO$fePunsaw_^nn`w-H9|}HF-XzKRaToiCptO+X*`3Dz!pw-X;5 z7itOzr+8`j2H8cDYYEt4omXGLwh-%$=vKQB3Dl0v_AX=+mYF(j{E8H*hV%*b-PhQB zB_H)sxv}}UZZD^auUtTMe|ar-;$C)#p$m7G^$ZdeN4sev7dLPb=17$GV`=AJ%5=JSx;^g8L);i9rSnz;Yi`k5>pcKI|6<~+lyfd>K$Rr zi$5Q#W;%)6b5#Mk8ymmLWkE}BGY&8W-r=g3O7Pwz=Fn*8UCt|L<)po23+`XS2TtG; zk1V&pJIlZ^WE!Hai)NsK&v}NVHgCF}Jl6lNAX82}(lM9@{(AwcSM2B2<9Po^;D25s zW2Mqnr#!ZYzu_-q61$QE?(&fSOw$jrwS_Tw$4&`S)#A1Co~lzoGMs;sMf0e;N_O#{ zBd7q$PzQr;@zB6~8Iy+8C+4b77(mkr!dGmAoX@T#a4wy;eY-}gK|t6`;!stX2y~5VY|v%_N;&h< z5{nupCc&jMYq1F|8G43IMd&kr$o+wl+@4@~K5q|8d^-&{_u3P z@{v;FTZ$e;2fOkKzd9uX|Wj!Z42ZVL|UENtgV|ClJ%Ox z8bwq<22=`LX8bi(Q3Vy6{Z6Hvc+@BRtfZR}xqWj8V&iTN9>nYmyYY#}9qtNM`HxXL z^k@s~+*;UvodpeaPMG@;BzZHj*JVF*)t)IpWm+(A{}ID*V>H@qp&gHL5sTwFN;MT+ zjd(Z3J)LA_3@)AeLs8zgwk#~C$`hM$5=)-#e1=!y+()q(T_bLy{i7!BT5j1-&UoNs z2nPx${CzW)I!k4Iw=zWnF2OjKE1+W6CUxU)@$lX08HpXPMLkwOIg1iWGRW0MOq3S5MwK{;3^UU2YCW`3XNoS`BCR@b#dFUgBYbU|Q=O2C1@adX z68P7X?h+?7$${6Sd>ton*}pL6HYK*Wr3y(;ETV zH3RI{XbjW{-Kow?=0q&XjCQ_*6S3@;Q(~r7WzpFxdlUy;D*T<& z&2tOoK^!8~L|u*E(S)t|Mrkm`1w}<_W$yyRT!kO5UqtTfCJq{su0=jZj4R;C=qHJi8CCH!Ejjev`e84tw>b4cqVld98tqzV1(Df{1#h1jldIIR1v>z zlp6BW;or>K2sA8l+en>~cF|g>!{VURqd!Dz-P|Uaiw0vyN~I$S`Mx(eip#!Y7#gY% z=*rfgy?-(Ftj54my9s>0v*(q^qxrt!`KXmD`_uA4yR%d{n3axRA!>@Hi=7n~tM`{h zsJ;V&PXzP)#v=&bvZK=M!jIx02JGAjiV_&=wB;9@;I+hmVN`?ORSyhdU4T@p5d zMrM6?h#Kk&wwP7TP9r>7s3Ar14G`wcij2!sk+j?tismwB;lUHF$iQ~`Q_VtIJQo&8WywtZhw27^6;S{TJ9z9dZAKc5D<>=IkL z-C2&-jx677R#p84Bf8=<7#jY#NVtiCg8x*`{iuFDC}U124n627AGe@ybr-btJ{$Sw9e_zb7xcwA%4a2X%J|(FM%j6dT zq-r)HGnwiP&p>VkHENw5$u8V2RjYRHl6?k4r%TjB0|Iv8xh`F>t*xkFSH56{j~Rc< z`zlC;OB^mT1(ezgu%?noirv0Zf+%s$E>YM5Xirh5j6x?hnwn99b7VA;BaPNYRHL%< z=m2p)3}+^>PDv02Ol*>`*3CfP7#caost6%6A#{=9(gGG&gyk@KRNNI6Ve-FibbU6% zQ1j()cJu9Cvt*hpvm51AKt9uSGY>doL1J|7@4Bqkb6SMw(AwZeNqDM1Vr^qqxxw5j z*ZCN9WC$1PGO#wwo$k>g%0Fh7a9aJjPu1_&^38%unwHU2YaIG1rNPvEN1BFzZxguL zUg}0A9(ioonXIUi1Gx{-A!+>k=P+D{jplw^eAk)r1ae;FhgRWFQozTzhg-vbcY(22 zd-*EWC9gLv&jESNv*05SO7uk6Y$@~2ZFlsO=~?nPv(nv%Ab7f56QQ{$P&)drCj)ai zBqbmxDVvPx8GBIDbh>c%gUrAt@+>!uR%4-ZY-158Q_`#k5sQs-GI`!9F+fgO3!pEO z{PTZKM+KW4aqpj5zlLIQg{dcM^(|@WLcgfv&UHT$GsvdEg}3xkm!x3vjr@r>ddjn< z$x9P}IK6O~{;OO8cD)$pUJMh3p^A0&Kml%qg>5tHzebP<@W1yzmm8h zEmKC(m4uEQ_?g+Bd3!=n$`jbBC3D2TuOkfrNOw|f`oqFJc!klYFFRwz|GAu4jdYus z3*I zFpOw~r1X;6INW_ukO67=9#Nv&=_A#%8(o6dWZ zbe#7SSN4*mN=LaUrpH0aL>h(wtRnH+x^<;m8s;D-1WW!Yv=&vSyi2t&2Y}(6 zHy-aJ%CB8shr?NuS>h-G|N}^FwAX44+0@Hk>&)G=7fg1|0ot?-qF!^wBT> zrz;+$AnES}v|uFMj!_B@koSO5@Mfne9=?A&@j#H+7iq%L!>LHqOUUzQX`~%MsHQmx z-Rq{PTcL2Dz&vN<3e3(TbY-PE2${V0p`<({1x7OP4jWq$A$gvHCt_$Z+LE~yr0EH> z`F~msfX`j;zuA#=2G$CeAegNq?;53w;2JH4A%?(&M-zB`!^1iq@5V7Qu?>3r!AUhy z_sLqkLFAJD&OxcBEDGlRc|rjL5@ygc<8*YGPH*`DUGWs^e@UARJ7kPc`e@dq+KQ2Q zFs^v)5@grq`=0X$X!j0AFuq9564&M};%gYwWetlad<&SxZx<#q==t31juP(a1~q71 zpngXUk4CC4D-3RXDinhSkwNn5*m5#6s!xUXm<>#R7yXWXfMuZiof(fuch~fK0zm6N zN3_k(&Sx0g*fD4Um+BPin`iL*5yL%OG>8=8WIdTesRF`s`f`#v%`XjDsqni%?gktS zfNi&^P-oC>)MX;sQ2?Kes3_KT^WHAJsn3;7%(s@Gi3L#1cBr8&RjHleob~4FW8va{ zXe3yDe!mktf1X@=tV9yec04-jb^W9)-+gip2t>!BP1Tz^9F7+ohuWx%MdBg>qGRbn zjlWog8{YN`>2^g$asT_}bourb3IF^X!hG}L%ksOliUN9eS*hcxZO&j?WyAd!7Grs- z+cc6(U}MC2KDch=DMj?7!r0e+YWP&!NfF&KuKvHt@NY}w{`LrE?Uvif)n@^p`z`NA zT}rG4*~Tf{Zy-g_YPz37*I_KS;8%T1jE_|oPw){hA7A6tnDZa>uA;p(@oRqe_o5IkQ!nF5)Yt45Q|YMS^!e{UEpYAEWT0B+;>jn4 zh~ZR-CG-dh<=M5&nv*d~fwk6y7C#^mfpph6G)mpn&Kzd>05ET{nlRw9<6;>8*w|ux z+4!F-r{z>6`(-mDVG(@!`+~qn`6gp@T=|K82i>vC!i*N)A6n0^YnoycklOY%a4#luA&;$b*O!e*)gRAHDbp5^)WW?_u0&6acu~ZcaAlKsESqQG>u13A&UPG#4xa|QVDxxH{x^BQ(El9(wzEf31hb`?haQASWNzc_v!Zv+0#L*V>AGCZ9oYZ zT!+J;+fzUQs#%DsUFm6F^dCs|)8T3QT0HHe6I$^xK(626^|eI7-s*oGpcBAD_P3rD z%d#iTVbVYBqv^py4p6f+%(>ABQ)@7?qAH5^-v|&i)I~z@X5D~d5fk%OLI|BfuD23tCHG&$%Zjy%z%BrJ3qi_^zkew}#cnDh`5g*8EjqUzU=s4r zjgS?E050YWztyR;8#hJ}9`EmmoDe}kF4FH5!P*{#*Rb}t)VY`z1bQFa0MIl>k*Cwy z!<#yYF*JnwsbE(}XEAS49B}rR$!MB-WZ|W?(D|lD!0|<3n8Ls=vc9EMl!*B*qQj{< zGn%n6%$Nfb$q>#=!Y}LlO6AoBP~e)0M_~u@Sv`UX;Y}AKB!4Ki`JLy%4CK=~oJMdP zjg~rnfk1Bg2uP#6O*;?TF>G)GwpeH${IjKzq44*^YMh0?bcYVam0tWV;vS5Yh`Ba# z!V!B(`7q%IESa3f0m{T&c5y*<0WqFc7;wYaO_>B3=lfJ0h2fXI1G!L;hqAwKD*)9o zxKI`kbDxU)QG!ZZ{x*y}TG(uE-KUV>L5jh^`;s)CC8MGU>*6oi#wxOSNHcV#83fJth8UKIRp?gGM)1``5|ed4ElNoXB=XOY=0H+qjSkx%lx zn=xT!d}@s2Qq&#AK#?!e+DVn5HR}tQWO&D#B*y&ByQ-CU9CwGuWJ~jncST z3@w>6H5qPNy}K20(7K@Ve&S9}6$Je$uphH=kp( z+)3cO@)~+zW9r#o;rl)pViK@@BUNq1UxEMnKO{wc{~yl(n=JMJDgxc;Apf%INMg(X zWz$)M=YY~>LS;k!ORJ+S>Adwn`34EVcxo?tr$5-)3;4m0S$r+^q4ag~HTXe170U7E zxOshUc+fj@qpVJR-b>!w=!@g|eegZgTl>`*VEEnp2tGG@miH#OD>U#O^)2&N|7H1p z^0@QS%l*|Ny!93{5kpA`Wp9WNJ2dFo%*f) zQTzG)o$xC9CA+5k5%(@+M0{kp@V)$H@b&%K91!{~{l)f-|NQ+;{8#aU`n~qHw@s+` z`&9_=9UH**Y4+Lu`So4@0(uI1#{2>Prntkk@Gal_>kGlZJV5Vl`R01$JK+l_z~t-i z=j7ba5h3~y=C9Sy>vzS0+G`?kZ>|raBhB)-qF&0c$uHtJGt{oK2h zcSHQz3-YfDqOXk4qEF)Iyvw}X-i0ql|H03w_t7`StMAS4bDy@4sJHLu-n-tv0c`;e z--BP_5AZLRw}!_%t3t!yhF^o2aFl+UN`mYm|t361~9BQk3Xr{9AZv)?22=j`z zRnQrB!zHYg9B|#4obZf(YZ*Nj@Rvozoco=Cq>(JibA!Ae4xA}>*>yUP&t7gD=#2kX zQ$fsIYcQ{TG+xxUd_qxB*~Ia}C9ik#FL^t&LedWn{YSNmmL3|S--mSdPsqiuBO}3R zV^a@xc=KE~0j> zCGmwAmkPz9U6I6N_K*}F>dAbD6s|?}%Sgzdf1qc&gFDxM1Dx=hM@0{3P;FPv6GhR3UnYisCK_7x4Z zcDMCsXVg>RHPRb)kl*K@krYVS(o2`GM2ZM4m6KX)ug9S2t21F1=u0@E*(y>?=Hj`| z&^1K$K%HJ5&7D<>n{1dEMwS1TZbl_~3o2;^eGZ6CUcDLHuWH=tG}rOTjn< z20u>j_QManQZi3r-pWy7a6bJ({RZWC&3CpU4p7`61Psr0zt+)*#xV0ePuq4HtludY zzB7}dRl=HrTwTXwJ}v`*$HoPnTBWXpfvy5^=-s~90(pFVMzPl;_eR!o@$iLVv`6o~ zW3Nvg!UOweN)72BJ?f7c)a3T;waBkbemCuVEt;bmkh{qPaOPh;oaRuet{Cg%VL~h- z3uv;ORsnA8f1);Zs+K>7H?ik51%=jXxA8asy=PqMxsbD>jyTo_WjVOy|J_fhJx2gb z6M5C>!pQsZ_K`^%kkza%iq^}*F%g)R%^n&&X^=-F{+Qd1)pA&#Ae zvi1q_IKw%{UmnXw!M=8*$70{QFdxgK4c`*=aQ~-!@ryMw;*@7tdu7s^XsI-pMrV5= zoZh(-J3Uf|{w^6z#-0Li4rn!~QC&?Ex&JWXx9l!&Y_$4W%Qg6~)*oK=%a8tjTec%9j+6z~?I5@n4R(6xv zf(wBz1-^vE?q@KtdjDZ+y*Z!lGT60d`{6?)+d>a2v_H|ZZ8*(zYhoQdc*8jvcCZuu z2rViZ(JFn>D%{Xv6Fp{)BMP|mWd1*d7O?=*A-`E_qdO4zl83=n{ZiLodYM3Lvp1YH zQrUD{j&i}%Co|%@iM;0#u?=%YcJzzEjUQe$B;`U*4Y{H>?qf^<_S>MnCEiYO>Gz!3 zZAsifF;WApHh{?v#lwSpsVz|&LWj3hB$@YXyjzi9PFjHf=DCs1BD99uputPK%yhR^ zXUrE(nvTu;7Y*zd?d~W#&gl6|*Qxz{oH{h$2<{4fHOtAEYyKn!pUN@i3qBL9K9jRs zbhl_YuLLWko~U2q{11oq_iRJ?eTYD<@>$*OaR^B5q*4#~U_4hF3w^Sj3k5;`|p2j zeatw`&$8NzuTtS#UK^yi2>!yVB6rn)3YrTagNC)#?-kDBVvmfzs>tiCGc^=&r^K%L z-?gF6JA~jnbn0fxj|(DQC%ug)IYn0L|0usTp?9HotJblLy~OJEzo|z>ar5?~7jK|- zVJsc6qaDYfcsh6pqmm|k^J!8X3O`zXtr__ZX)}kR{?~#LPb4rqR91g)ZUjl<_{hiU zChF1)k|km1#_Bw2t0h73{eNTc9fEXW!Y;wGZQHhOTeoc6cHOdV+qP}nzGWNld_Ak) zOiXVkX7gv{BG+f}#L3KqBHbT|*xlQi0@q$z9T)5^xc>)s_P&i{kJ{~+)`2>cHM|AWB)PY4hI0sR8{cK`cW`~P<70@}7c)~W>0 zHJ&u*p>d%0hh5cILOaRqhu)-*bfChyT6LbgFSBJ#5xn7X{zj=X8lt6k33=jlW>r0A z5Bngz?7%Pbw8&QJxYb_g0e8f+bDEkl;*NknrQ(dh=iu`*ky11a=f=E1y|9`mi1_9a zLb#W}^gx~|lPJ?_?T%j&SP60RH_>Sg0zsW|YfhjsZRp0@r|}ExPhNG{$c$2V9S-ge z%=-HuNUn4DQGV;ktZ*&=7xz$j!_Mj{S_9HSYJL?C^(s9UnQ-DPmsva7+ROXa`d}sFodr3W&hU7Bnp37(rKDQzG?UlFU=Df@h~i-H9mgL2BE4|E0N!vRGHslA z4eJW@t<3_R3guN*MMz1GH}Z)w{vK|-7oh>51md!ZUB1*k?e{+o+^$$WE^o9hBS2>6 z_6MNloNZv?+bf*p7aVYKwm)CCzvI2G^~#ru{5LI<=%;gbLi9op!Vxzr=vR)goI#ON zYe@@Cj6G?=!lJ~Y>gzauLV+9LAr<#x^x%QibL!fUnfDF#oCIvXx zi*{8Kk#ah$C5CXw0F>jN=xaR#$v=1l>IE%tH+h)^gwu$;OT6SzgI;Yynp81LE(~1L zJ8AV@e76oCmq$zl*OAFR!U`lho8ZN04aY!jih6>Ne*ZmD@y zb*Q*6Pb(qP$0eZiKP&GE+gAZzLsF@|6;<5lh>q78LeHDwztC1I?JP24`GLOEB&c}Y zPx|_LBh%qyI3HHaFvl%#<7}tVK>k)w7B60Zm7Rv##Hf9i;;^g~cI+OiQ_H{H^FcHJ zMv(J}T}H(Z3~iC&f3lOqdjkxym7uzX@vGJ82;d-O$iMQ%KKXCVEw#Tb7yF#}BIbnD z!Ot?XON~t8?1!&nAzu;v@RtnR7=EQ`=99BOKoaC^FMFMGj1cujY+_CD(ZYx;z2pwY zzkG53MbBgS`=+uNnW@A4@EGek7w#t1C=iag?ZX@F=>1s1#52L>5A1Dsc6%Cpi`kYp zLl2-c-o$ei5!OOudmvFEURIitG=o1eWrr=e?Lrt@^$0z=$FtfMi2;AlI`%!ID^lm$ zUw|DTRWfr0;H>}@zpfp62TsO1t(G-J5n>31sx{8_;jM4KdSPWj_~W`vqd33rM(*Bf!2Gbv)_;X+-l{0B9Zz$P?n67 ztrkppwK(=VN=3kGdE;pdqNaSN+P6@I4%Z+yR~KrNE|ie9{hZ2H#7}iUuq*egt@gqR z>scVeV!5$>VBOwn!iYu{_dwV!9jXE-@Rnxd7*XlYNA3Z^6515HoHw=8nWEpfvGBm? z2p*(x>N}p{0OZXryU$=5)%a1Eg118iO>{WU$uUOgspk-JZYX&XRt%dC8*8u;TeYei z*0=$=fi!>7|rWr_g*W(T~{+i1ja1(8)vyLi!B@WZUEZ1uSpRgsQ!t3V%cD zE*NIb((iB2N||NA9f4~S_Y>B0I3d2o?$-^6LC&ioSI+2v^^3~|t znZAbojQrgqrpY4M>j>_&`n4=rB6FM_Vg}036U7qzgvw0aF^?Rh3WZIVql#*xcc^?Y z^&9Y1dVMzh*VUj+sI2PI8HCQqR;s*#vxMTN-6n$M8!&(4W-n@;P0lf=K34|OcMtwq zs%B>H42%n$fnF09oY@&CBFhH&X5z-UO4X$iNa!k~VC^k8ef}yUdUHK2ve~&02Yv&5 z9B)bmU^rry*HOO=| zks9#E{Ka4OG#mO|k)}_h2r;xcZMj4g7BL5i79UFW0lPZsh4bbGO_O_U@FjUM3FT|> zU56#kpvB8ZmdBjX79xT{7rp z^DPyiWj%Jk^$cx6bep#fBPRY&a3-SbnOK*Me~qW>`uz@rQKwja6?m`>Xk1XIh~8-l ze4a&ew1=X1sng6^Zv0LtzSsE5^Et1Ql2`d!QA;kn>c(O9j-vzwjDn~{dibqJM}%b6 ziIw;cCz@#j8;xhL2(mc%h7|C$iI?hidiM)K&Wlt8DV~)TRHS9?wiMDWHf*#QPn0D? zHI)PtdotF$)K|H?3bitW+xpv(h}^=BnX&`ILJY2;@06+lkgJrYB!LL8sJAT8E@!CO zSG$}H*dSZY$BX}5G)$kGTte-2f1N>5`RPrtSK*(teZ(KTecM+TfS4mgRoGNV)KHje z#{hY)g<_j~+=x6v4UP_*#Ne;F{NGqhqNoWVrcJ^>5_}%9*d~jTFyuzU zA<_^)Afi7IZZ0=VHB^$OlwDrq_SDMA%mVvbp|^A8NE?bU@J|bd95Z`!_^l65y>J zG8V^A*;8pyk z?64n^7NH;7oY6eTn+^Y%q%7w?z?_e^+Au7TGKbT8rq zZK|v@;Yu1{{quSJPOHBwCqdwj7T|wKUj2t}|3+RbaQTGn9Q?;b1@L_)IGz+BNsY>Af^#F^Uw=H9~m+#UZSnhfunS^Ul@pLZEk{Uw=Oq~hB0t*#<;Gd z^;w&`v7vSAR)ocUzclV5wV?lhE)<2KfypOu{EWd{2+~;;wjdSWNd}%Xx3-dsfM{@f z0qy>@7Xg>U1MPWbX)AwejekIoMjETB4;s(lOoB-a15JSh8{i8Pp}gjn(Ux-U#p z?eqh;%M7dOsNMC-OPBLI1G^%8JN3s6>pPEz6q63(LvcBtDx!0~ZR@+az3Jc}mPK#e z4`>?UbKZ*5Sy6f#mh>`jEEK#6Bf(f=3msG`7df2tGn+{HTr zf%?t?KM~1Hf2b_S?ak`WwDJUn$^DUlkW%(st+ok@rKermYS5_bd~NVLNkrj&jxwR) zSngw(Bl}jDG3%O9sVhG_z7H$4Qg7F#q}hwz{NVCClx5 zo?#ty){rH7lu%n}v)lR4E8aDH5$_?iiqGwp$1^H3Rh~bLf4&|=(DykL2xF@$a27i0 z3r|OnK#z+`_auq(uYI)yiR-aXPvREt?+Tn}0SMcE)_?^C2RE2Y`nXJ1jv4WA$XS?_ z2xP-w-L%n)We%PV$27Sd+W_^lHb^=>j4?@-X<|2%%JgW-<#;4e2wd+&SdGkl*ZA|k z$8#=EZT1LJi=U`{nWl8mfwmn^+$Sr(e2>C=h3$_Px|_nOd!+`~BepbUlYK>C_3UFCT5n>h8*t+gxG#!&B zS1GvfxWB!*r|=jRs(3t5Er+qmo&E(st*Smhfp~>OMhK|2XAr$0qcPMh82IRMe-#$& z+4o&@ZN1Tv$O%igOpE2Bnu6=6xADiS!>Q@2d!^~Pjl$q8I&uXwghl}Bvf+`!kHfem z6N>jLD~;f+HvpVo5u@h4S8)&FOZwnHZ5JR;L%lJnm#x-!tE4pXXsn3#a<@exj*C4s z4WZzBP#QLJ^%4^YJNQskbK%~qH4u@T8&{GxT@bTBb`XxFZ$Pv0<{bp6p7`;p)Vk=S zsWvRg6%wcwrBF})-jI?rW5=Iwo0`={k@cyncn^P;&XVd;`#^D~C~5)a=4@0jz)!#7 zIZ_88;Xg?XYRUO(uq1Tbk;1uN;;lc#Xnz%Kt}Ltj$ErcTRcre-bKo78f5*%pi8{yhdreUr|JA1}6Gytdmxn zBOSie(EJul>r|4BEN~E2A0NN;7F7STkU9ys4fcLdZ#p(58G!N4I6gRb{M=H@K4C4p zb73xnh@<$zoLPZa(0SAa<^Q^>P_-UFnbgNu>)u?(eBl->#_-s9B*#t=P)+EMbrKb$E<=)LM)rWW$i&s0z zO}s2Pm@FjSENt?mo1J(UQIDN~Vya?3fc1nbbH!sm(*hHA^StxWb6n`LI8) zX2-`AJ(pJpglLUW3>_YU^xX<+%kc7oI<>P5CcX!pOCjbDYat|32?c~fttcUmHF7ul}qmm~Zcl$c@Y3tI^j0lp{31ZKj8 zlpC?D8nnGO`MkiUPGE1OXm6_Qa>L1JPp}?m{Rm|OA~djdB3+O=*jL>kR`VQ->BPi> zoDM~)q_U932z5t0V-93HiCs7fRGDY00GFJ;MxzK!V>4o=VM*9i? ziVWD7(B)Y};xayj=;{y{*Z#v{n)4|)42cv(jyW+q_{S9QC6py$*q~&C9CUO!W>n64 zsj(a){e6hPJI1G0eD8qs@ThHZt{uA=4xUX8rI+>15Bg)k&*zOpx6uke9y91#`ov`@_&JUY zi+m!DtH9dg6H}zunOS^uA~FA=JOZD}!3cURx-KEce`I{ss=a>Lah@pRV44yTS(g7( zIh@yLk&RTAu){zN?tsmN`T<=RJbURm*tjAVYz}&wg`q{CkJnU))EhYm|AFXT#-3aw z0}0}u(wh#wx9U|nB6Gzw5SA8O|2z|5kio)BH=IV>T+}p+2&M3u$iJrHxuoi*k7*-i_wL6VN>{B#-1C9w6Me5RSl0dJ$U5We9LNk;M`mH<-yw z9av_5IM7}j{T4T;F27i_VFO01@;pbPL_l00Y3il1)Gv@M^!JSf3r4O>C|gnxnba_;dvLx?rmQ7i4_A)Rs7chmpevEl^g zF)o4a{f)Vw+}o9=KByXcUbGB{WNRux*Ipn(djZQIQui2=@$uPV4qnb}= zj%`~(-nY4yAZ^aJn&`jZ3PvlJDOw)+LL*f;0y(x3O1K;x} zJfvrqkr#a7-~N7ex5EL2yyLVW(%$X3@z)kQfnLAXIFv*^A-OoRZv7*Xj*C`;$P`V4 z8_1PY^KRdoC+C-38IFcNEJ#*S^>mxs4{UyT2mHPLggx6!YIN)X*8AQSd$W@t!o2`$ zueRd-G@-oEG?#e61N@a2a8=krw=D|Hw(&)LQam`5^SwiqTA{E~AeY3A3wVTi0j?tE z5#-lQ^OLFr;F&*Ay7m~R87eTv*aBW7D#Y%QOh{;dn1!qzFbzbPkNs91I6AuRoL@Pi zIP~i(N{N73s`f#eA@DO)>5PEkOc$2@&-T!6{f~pE)(I(auSYd@5;Wy3#s~N0&h_|Z?Q(+mxP7QNR94RZ>6Cmg2zxb~v} ze*fdzS;gOGe=z?X2(54cyt9YXJ_;))cuqgQQ~12W%E~<87uPX%=>6guas;PN=2Qi; zw_6y~wyDqjR6C)&eUj|yo$6%d0?GlB2jB)<)>x&;?BNKr1 zoHq~MN)FwAaiePFo87y*#GPy>M&Bbl;cvD-O66y`0CS4RO`=4>_>UJ6G)P_jqg%V4 zS~W<|mi>|#9OY|^B*rdW&%W)Mb$iws_IIsCg`}6&fD+9m%tBA&^5Q5isLX%xz_wa) zLMtyXdurLuf^?^~-rC2>A{1Mrmp-o3X7b|IdVRU$r;cdLtFk6t4KskpIJ?bMdKpj& zgiIa?hE*)M(qgtXmPk#l>eUTzX2)$Js!RbMNFvYdj5)r~iq0==!{hQ9cm zaFdog1XAC=@D}wRLo?oSlDZA*%K`X95Q2?z@kc(I3x3D0VEn?Q&<-|lBag@MwHpP7 z{MB!J#f)1I_egrUxxNL;+nw+l1V(qEmZ7%JF+fl>)*tcbB!#5K6F*X~ON>JPkV|sn zQF70)?~dY?htl#~^$*LcrC=sK6d`Ynx9d2l>ndQbp-Eh#WI!^zsakst^20M|b+a_F zdmR7UuZ26S%EWtrIqUth=^**SrJ4vif{6G!)+gt;u9G23+eh=wOEm%GflwYh08}Ex zJzbNF*4&ykyR31bghK)wT`3a6Pji<&eY=#zHet{A(Wq4V>6Z5>ykf(SF492RL=&?g ztLP(Evwm1q%A@B7onH`6?K32ym~cfnB5%IgIT6StWtDBy4&fKm|tNSqSjU%G>d$mXq=k%|gK zugTiJV7?{z&k4-Dq*e1DPyRQ_LFcP4NhKmOr=Bqe;m?k#oA-aCeu*&8S+nnNRmkIB`agSQmRKfA+64dtDF^cW(nWFoJKKhL zvkN9GV3tZEYpPz?&6I}a58jm;DKFgR{5oOg=W1Z{342ed%wd`$Ra>iD{bdNqoaCG?lnoz7NDvZb^rTVd`8_$!?}gzv_(0jlvK9?0+n%e& z9Ygk|LILnp!S9ypO5)H|APF(1g)$fedN663KObr`87<{NB%KNgE{Mlp@j25r8n5j` zV3gOv3|1Seo^uQ^+d*5K2JlvX!a;RuXKAyA%DT+N1n;_0rs?^g%Co@W_+QDhqt~Tj zkhcfuNS10ZyjM#eL{CDphl~-dbn~%X7j6U!qEjLHTZf);_SMm=P>CS+;|}*N>VYhV2gR#EsW-4C<-30vck*Lc{k+ zt`Tw$F>BnCy!nR71Hu0pILs`*j!*K+vK`JY(SOR>+K7;^D&2f1^L1`G=_} z-lGknHjmzO$K61xiXGfflIbFVAuUNry(LAYvx?A{Db!G=_>*v%5hHs&PmKmFfvA}} zEwEK7HYLshw2g~e-pe?T;Wc7sV z#YRhv6;jni?+%K#n4)^#;Q5Z*xm1FmV?A#hj~;zATn^MVvET17eD3&ti2{wba|Kg# zkPl$-*wMRJ5#{~W^Z-!$V(fsEZxXOX1S~GNQ4|a#&O1zNXf+V)koqw|ifh=P5*A@? z-%ot2jukA!g&QZQ5Vrs1({!^qp4#+?&?R4nFr2vso6M!xNKE*Y^Y#L*39VFBGomPt z;rovHW{kro3NW+eq*;H4AhqV7tcfcg=f=`b2-jzXRe+MCUiSp)wwH%jKm;-j8xzMT z#8I|0k-uUaF#mLi+(h~a1_j{{`Bto3M^As~`=)F8PA#^EYkszhgB01EA-$nw-uDH@ zY7I!(DIRNAI;_lI!ZBfY83IGPre~r<)tmq0NETeJ&N3fkBwf!1VxMb@+?&;;3y2gz z|12ASSYG_fL8q#cw?>SFWFsY-KL}ujVVkD=(Hi_+D(ev}D7gx`0dmd@dTYKmoQiw?wJbo1Jm9mM@3QMUG`; z6}mUqum{KswlsVyZ76db_~^V&a{#*v3F4G52pyX5>(>$8y{Nc__!jAyXI+8zXwN0X zKMsaWZ}1F$&tHS!+130W{wkh)s&62L*aXF zi1T}i+$J8p+W%3P3uqnOE-{trr`|r>p;!y8dRyN9x2I>u3=sRx#3MvN&G*%^CLYVx zhV6rbWg6p5%la1qTgQM8p8U#gogI%RkL5ds#F&RAGh4_}*wfhZLxH=UiYIclc)^;3 zDiM%LE4K82tbBnT=S@}Y{H!3)@XjNn^%NOj^f?RKftI;_8jH}ArybGw=X|J7U1I72 z!lReuLW`8on4!q%zdQ2=hO``1sQI>zPt-2LH)7zQ{Kr`2u^qt8<_az=az3(t zP1If}r0?I+wNj9jguR86E+d%f6}gjA<%79)^6eNb7{QpEE46w%4zGP9#QSzM@vELY z9PO%emT*y9!E+7_J+aV~3!GnkhloALMJvybq<#8N#OEE14AH-QSUK6Zhf}QNOG-1~ z2t&QaAj__5^FtBk8|)`tgwZvKQ<1212+tWUx>-nV`_)dfamx*BP&pk9KLdVs_VY^T zvOau*F%Y?*%96tq;Q_kWzAE(Ubp&IuC*L9G8BY*_l7Xf>P*XZh2O)LhNy*+k!c#So zLfm!AwoY(%YLOkj6E}Q)P(UE7umH_AeW~Eh6E`_${t_ai1$6NMLY6+eH7)VYRTJ1W zDsGuMEMkeqLQ@H;8aN5d^Ph2f8tP?dv9nlnn6C_v<*zCwWFm%0f67n`mp|PUtW#PLg=WbGo$z$&uAHYGr5R_9_>_3jkYZ)1R5p* zlrx?eu?+PpE;u0&El1{ydu;o6O^PZ%xql!+)#->2IKXF-$F#Slm zTr!h;8uvAaWYt-NTk-B}mXTTV9ayj|{{LkFDCf@XWy>NaFi`#1ZUi`OIyUNR8P@z2 z`U|LisNQO(-Bg;bAwBI+f%7T?sOWJm?+xPYDcpt$uWW%P_GR@>RB z<)>j^*YJA3m*5O5tWJJgnyXmk{%u>^YNDM_DL71-=P)^44?o;kp_E1-=b+3T$0=YF z9i#InYHa(}7H(DP4~4A(6MIJy6UkaGB#>+Gr}@z`t%+0(O0C3u>dQrqjUTX zZqFXkv$%d)`}<}N-YuI>0T*D#1QY2{+7vaHBiN{So0zs_*r8QoL&nt%f&zOmXd1M{FM@s(gUG_5G(sN!oE+Ht0DYqfQXS!tFEs`_+-YDG(?bgX+{n zq57o^2M+LrmUAAZja@8A7~=>zBWVq|$Y1c4ibuzPEjJwY0Hyj13dXptgstuO;>J189Y@!ha{6vbvOe`PIz&RKv z9d$ldi{sf10k-zTX%3Q>)bbLABnG%}oyz~3#Cea29h?*KWTOlj#NVAB%yk@?D#)?w~fQ z{fdm1te*E$t?7qy11KmG$Vu z8lP2QxwFe02M<2bdVU(X?ELMdvt^Mkqg|UFX5|dH!%9z?l#%QivD_{q$J!o24qNUA z)x6H(=gITLvk5Uy95ezcbUsw*O+2*Ls2#LVr!R150*;LbT&aWG+G>|xwvHkL{wsJO z4nc*Z6KljAG$|$AIijM(Uc37$HT(i_aSLvrs^`GIih8uj9bvv5Ys!$TzBuaIP!+ZB|GaW zo6GZQ5zi>Ofy!6W*0G0Jc(9-?eh6}N#O-Hs_*S@!DAtKj>EG~EfOEJ``D~sg(VfgH zI$xj__fsGhI+FAB>u&a<<&|k3$46^o))W@3H--iUknTzMZ*jH+l61xJ{|(53SLP?+ z<@&@zzZ0nC#TSIEKetlhez3#rs0=BpsI#KwlGx#6*?Y>U6%>qG{&mGlA*ICeWbVK; zjDsx``_E%&&ipD%K_ydarbF>x4P>XfsCp;qk&g%5)WjldMW0pA=~ogv4rWK5j-Qm~ z(TLA@cXPx=LpO*eVG(LQhMVJd6eLh}(oxi)jo*WD&ireSLd;BFPxGuY#;{BX@8bz? z{MU)sKq;4tU`UQmR}#%J!DH*U4RiH~%{|P(YEn z+Y8aoOfJ0|4gDse+UNC7zR#ye|K?I2A;RoD_~*l4c$9dV2^GdcB|)In#1zH8?N{~g zKR>^_mz0Ega8}hi4%>ZE3F}{|M;C`OqmL1&42Jn-2vNH#X7V8ndwyw=(Vxl{2_p9) ztr~A{QK|_ng-t^bJ%ziqqcJ7^Ez}srKr+|hK$cI@C4J!e@x;(R%@brO?nd5daIzv; z(EpN^Hw!-RO3IK7Rhi*UaY;}nUd?v5D73u)HWJRk1z6|m7 z!#rK@4;udIv~#(NI}~R%_|IWin8++h@+Z);Hc4lXVWAxOqUKH@A3l>HvM6n0eo0J> zPkoM2vWc=gPG5o*S@V^(4!o+qBTN5Sf_~&WvYVK8t2IZLx195?MW2HnIHb(>*MQj7 zq{HslFlUdsYvs&q5OlX%Gq29Rfymc_ewc8O5OmxeZ^F%9sr-v#6tW|}i^ODv_P|m5yrj3K-Vo&6T(;0` zEpU;s>-j{cG7-Y0?yI?yE9MV0i#_J=59B?ve zD|olNL3}|7F&=^Nk5Ko~M-VZDQJ1@A1W56q>|%wacS@J_^n0sywa<)cx$;c@*M#_p z3cRmeKmEK?#jkK>k^=TrZSW z#Ggq~Z|s&*V(iO6gfuL#Sc6kK_f&dKmjfmYUeVMFQ!roQQpfM?mfc`2x^$J=$R}_7E9EVUZ%HvXFIaCf#cER7#Y{puZK{)lE8k9N~CV^IMG<~JQ z7fp(Po_l7J?%`rDaacR&Hs!-B1$0F$g*E_Vk%NY&V^4R9nm6Wg<|gqYuEz98Qn8Zv z=kCX6o`Cbu&W73s=C`(zQp^p!!rmMD{#fc%WAxag7HI+9$vaE#!%TFO&IC|(_K(7F zJcSzA#8?r~g@z{bILv?0_muZ|OjTbr%V$>qN!zaWc{lx9PvMs2 zcx=95;l{}nc=DqWPjw@6XWY( z6;j_vS1ujs-z_YY!5QrdnK_S~UTNRYj&r5dvRM}SER**1*-g%sL{)>UFI6lM)rOL4 z1pTT~kp$~k-PQ?DV1Zs&FOWa;w>OpZkGxgP3cvkF;^7FndLKH29EIclTptqv&e{({ zp+GyKW>*eI0e;(7H?7`^9BOzmp6kksI}K`)f_#2B;8);m9zP^^``ppuG&t_P@1Uo0 zCZP9?Bo}H*!m87kEux7EuVruOYicCl^BkdmxhYR%xLl;zetGecDJGdI< zu4~>SB@GHgB?ITiSk(O!jJEHQo44k#exo~#sX&YN9)BZFFyXghn0ZLC<~yH~Al1_n z=>fK6IfhAC`_&qb&D%phf;75q3%2hhQbFbjkzBV72kF!C1SsZFKJNUmuSU_Kh}Gx3 z%v>8gd87S7zcJ3#S<i^bSKY=e4KS-*1u?c9CeFfmA{ z+aH8z@3%`7|27i&fW6#bQw&-`lNO63#5B!7kaf;+Ke4>wCjG$$!UQI8?#tBOboIBQ zct|bKWL5>URArFEJ386?Y$8G}Td>HPJS*H%LJk+38;aw7vV0wKTzUgtanzj?qb3#v zq5UN|*3$fqz!!p&>>fa%Qld+$fmHnHlHD-#Q;~}vr$h>&mKX^7;Y?lO6lL-W(` zyh?+kho-0frnhXx0lgT%L_BI+5H+x%BVy$y=fuRvq?{B}XvUv*d?8JAcxW?34&Uda zIZwNFW*siqKFIL(d#|$y^&ci6JrY-+c*^0S)H$-ug}tOjBnSX-@z${p?ubttzdJm? z*|iu(at63027(+A=%O_$Sb3j>c?3a@P#~HqT-RT*bsOO40%w5(Jqb&%0U!6Gqy2^C z0Fyx2{P=ZoWSr_IoFU~45m|nhpEO;(l{ZD?qXB8M^uos zM;uxRi&JvS`)QYBW)F*`jWJ2wGv83~nsm5+`_Vp(61HoOg)QVEYDdsbgNvfvZwY~?d<0?J~Le{b>kb!9oSAoFn5+6(Z+mRJg%Z&M!tbp-ES>5RL-dZa| z1E*ayd~$W@sE+o7#1$x_O($f9g?qV=R_*3&ED>PD;|0UFkuP?;40)$~2fI`%9$7LF zVZ1Of8Avxk3k|TP?>IHSah`h`oZ5_B0R4fK9gNYgRNlbuF1R&iycV9M36g*Ry^xz* zE$fsY)EgX|WuNIblhkWQ_rln{L)*70L^AQO07mHeLSqE$Y()%+{|Q<>%T0g-bI)D`{>vmbs_b1%` z{YJ>c^&&1PvUI5Y!c7_K!XcwD8_4c(^KW{8@~pt(fKuL}g2%Z-q!#t%uj9$Ap#~c- z$t}@`4R*pwVcy6kgfE?Dl<+oNzD-+(shVPtuXCAO@qLuG_EH1U+p4-_!mEsgFqB-L0m3TM6$vRN!Xt0Z* z&V1A|WxKJ{hH+c6SoxgvB&BBnpNQY2fle%ZtXiaf;i5C4l^j4nglsV zWb#A?6c19MFy9GY#_EF@b1~ivR(r3>YE9kBN>*jCnM$hC5T5_`=>8yOZ;1Pq?(rv2 zev?tWYvr*r5*V{pBdC&NY6}tP^e8kms_TnQ^u2d*ICWOG9T72A2XIjEKRmcnIi(W8 zTu%GXd@&T+Kt!vd`7n>{p_B`QYH8N8j9ZQ0L-6C4rw*`idyGLyLp1v)I|1X5$_pj( zfuNNLznu;RrSkS1Xz<^o7?>+DzM$@7RU)bwmmq{e7xK$Jq8ep3Ef-nOrU77WffTZi z_YBcaUgGAeLF{rQ60e_a{Bk5N-_bc2c>f)lHUG+JYSy>gQG<$#w5x`3LXY8^kf*E8u%ywS5$l6TqvK7QGx4#N!|EAWQF#yM%Muma zuYf*x#1%QN3hNbW)?q8z6aGe4@A^qaS{G*Lik}Ndg@URj8)bvelpY--Tp}-esReFUHG`R%Y(D)bh50z2u}(53%2=-BSwUd6eIVbJ?wGc&dJMbphO^f@-5B0V1Q=q<2t>moN3T;LGU3{|Oc5`F~?<@$VC5 zYvsIEq)+qA_$C

    Amh>fYl4luUS{hNw6Hnbu+0MTts*-fIz!ZZtJbSD*LGe=Bbn+|7z+(jhYnGA50 zPUHRUX

    oNsxPWWi_8Qn1gmC7%6bBGXJjRC3SMHYm$Oqonj-v1r^hGQQxblkGM% zy`SR-)Ajw=fC9w9ea@0@YhTD@(9KD{`CTodVq7miiaCN z9|VymHg9DW+I_glySA+6fPYDs4UF-aQ}t>$>8;aX7i$(CYI@=Bk@2aqb$m<1DKM44 ztCm_QzEWdM6AG-+OG;9xXg%AzxVvzh#g>#o@BrHGVAE&YLwGro&nnMCcJFw$e?1ta z=7!Q=EzoV^gnJ8*)pvof+pn^G%Ics&*0Hro1~(d1it!Ss+6FH|mQ7WMB$Y#=Zg75ebhXL2aMzxV zUnlNtpOF-U;>*ry8Y|9rMTv8*|7m+V9tuq%nh3-0W*X1@9Uo`kx0-r5ktRD+jHkwV zq}sgEcPFsEuA$yJm{*pAX^gPi{7Nvr*b6^)aCcKCazj0{ALY8VYtoNyAgIeO8wcJb z`*O?*H#(v~eZ&jV$vAPKOy}=^7fcrZ&3q(aT&15 zKI`m}jgubW#V2I9w(TdPC`fySVKn9D;zp@Z;S29QcP%J95g;VP<0=yXF52Dh5#@kJ ziWBDmW?BmYpKUhMI_vma%7$Vh=Ux6+dG6bW{_L0`_l2jtNi3?*gD7(e*ne?#Hc5egIgMFlL z@ARN~0tP020=7#1AfWxLxyC;XPyo|SgoI=DMI2F{tW-n(+X%hrv9d-?_^`t5+Y_kA z%yxZa#%OxnwhpS&!uA0@6tPY2NmNAeC+Ni)`l~41C@vFQWF+o87U|&7ruFQyfA4L} z^1T*G-$+1ZInsV}F-|>TLbR%$2VQcI!(;=xR*W8|5KeO~LOO!EUFukE^TO@-@7v`A zbtW7EVM?%BfeSNbAF_8dvz&z9PU)^U=0StW*u?$Y(ImURY&I&JR1R;zb4!#tsRgB{ zb0ku1?$*^Z{2?;?MHFc6PGHxOUNENqfOx z9lTPI>fW5bh0=Dxm4qCsmRFHeJ%(YSeo#3}#aw0d0o%!*0}5UrL6vvK5hze<@7mR? z&gsvqJ}hA3ALkh)Ske){Q}WNT2FZY9x4u=5%91&iC6IP^nc z(GlM3cxK1zk#ATI=n3wih$j|tpON$mVw-qdkmjG-2dJM^o3*z z@g#L0!6--B6(@bvF1)7@_rp{NxUk!s|2nhC!*~+J00dAYBOv}32x3NE_g#u{@*fZ6 z3r%cg)U_&8W@L35lj-r*Xq##6=0TRqJ}p$9=N~oNqUMRkbZg&q3+6Qc+F)}_)`#-@ zq#4m6>&hq_jLfq)VDEmrMHA*=q&f2ENl@=qfVQ{riAR9=2_6e2j4FOV>5$>MRW?=U zS3*!3j543rC~C62Sibvei;+$IT}UA5oHISFSCnW~JohWX_TA`#5u0@s^D2fV64)T@ z)jrl2-%t9)s0(>;Yix?XU8@9Nl7e%A#%^#UUUwI-a(kF0?Dwa|v1>0uwmS!6o2~FE4{OfNcGSxWi$7t%{D3rM?Mg+j9zYXqUjCW8b~*~b&GahH8l1t z6=O7&VANWXM}~ zjg}xU5nNP_RrUV`JV3+0Zz`t{<|ByE7nHry2FjbR1W4R-QTpqn@hdxlnrjh+3RNs+ z>m)l2oCEO0tdNp4?#WjQP$11HR6wR_Q} zAKHr7q{8zY$B`R_WC(|0@5AWv(cOd2LQ5l+=L=mDwNb`Rmu$wx@jSB<#t4d+!>aW* z2E#Xy*VZiy*Yp<)+isf&zjUYQ^}svQQs#3RVEYq=gw^Bn_j-M9_e+k^XyvOk7X=LhY@Z{AS5FH^ioc5^;}(RFJKO@ z<$@~B5dK8?#XaLaW+n}SV^+XP<#EP(L_OzfC{S(OSayqfan^3{&MxjHV`3{M*iCV? zqb{j<0Y{)u;wI(t4)`xfX~8By6WYX9zCF+w>#Zsj^jx$l3zT&m9NbQkc@_{&tqY?| z%U=`cdTHzr+G`|z7@3sv9*#WT=5vlNJ1b(?g%=UnM9?CswARt`w(YV#IUif!kRn?B z91V9uXC*&uFAXi;x88xlZHnQI!JUkjART!h;|?*#NYDUUVo;2AJmB@vW|!YJ5C&() zqsE#xg_%*Hp&$>RWfI(`CV$^%i8k0t!7=mmP7sePKjY9oxRj6jyqZ?E}?h zNqMGvTY*bgr<>$LyLMK?w&%D=4P0igFDntg203#N9FqnTg?S3igcmdsPX{|I z-TRw?2Efe&HFZ+Ql;v#BsN48&c6cu}x+UyLt{@rRl&jd*gdf+Aw+fG>${6JAVoFN<&enT^_bfv zDUj;Z2AZMdqBg5`XSl`uH`EDoN!w|f6e?nXTz=CC1_pS12lt=x4r~5jRcz+S2vY<8 z?rA`$6K?yF;*kEjY)xKO1$LV1fN31sOQhYqrd{caOL?f1{RB6-b1w`T#Be ztaKz*=q$F7_8syHQW&W<^I+1~wlpbvJPPp6udPW$rZn=us{s=$$y9Xa>ZDy>FCoy( z-kzYrz^9Ae>@h(;4M!ySi61vT+>lSXZrNx}ioxSk?B}FoS%4q``=uhEmEvwdiG$Ha z5LkJhdFX9 zkZSB$el>HeuK+RC8gzgB!SN9F;Tgaqo1H+_vv7p3f9es#Ms zs0vr8GVb9K@SM8?7$ds0$d3`SBU@U$QSJXX9FtETD0;k#3t!qBet)^2|HabT(xx}l z`G=+DZ|D9fcoBnu2AsLi#^1whY|0?9mXXCjy$5N6$%`lC2u2aj>lOuTt7?bdCow6N zm}-N?z0HX9LApdk(hAe-+zAA~aJGcrPi~s0RAqz>gaD&keeIZmS?D2SANp^d<6m#t z=`8ndeVGpcu9OSTsw$W}ri8X)02xWZ06nLwZQ*Xez4#P$T`=LE$BJS~9|9T!5SKuM zEne;iBk5Lz=p>5_%xsxfr5Q&~6lIF>r$qkE{9I2;ZJM_R=w6vDNul6Xh#l$J9@{bI z`0jUWCy;H%B-%jvSpv*af(X1SUrtd`qIv^)uL(<;+PCR;4}6yAo9O`BJppjG++|$- z^X9mlP9xK8$-l2zh%7BGl{=xwVz)U=hBD1pAKK$O4PZina2T^Pp>u4<0xKiBg=tJ8 z@?N3(weHl$xkE*@-|;$}hyl`%e{@N?8Z3@CNd&ttt@KvfXIl04VbI4}=x9J0QXgoD zaH_Z1-1cT zvlxe9k)9a++`GOP8N?|$-@=DSNqK&om|_l{$;0Z& zh}_%{6Uelvi**=}I;$we9Gn#;1C#FF<(#8+6%S~gwu9j&O-l@Qv5D?7t7!q5X`&PP zUn+K56#dtzj92?HlGrodCc^Loe&`OClwH%?hp&EZqb|nZp5!(zeXznKf|nk8(7r-V zDU8L*y|C$GA5l@OGW|efVT;o0`=u$E_c&n4v^Rka)C2IZ2qU@8p>KS>XKJVe87eEo zHUldL2oGfOdc)J3@f|A4#>Z73VLO0^d@bo@=o*A{2RMat27=&%XIDLqys=+?a9W(Z zhq<{dCtTCCu3(ib*>-n<->91=a2}LiIE?(q@lfgx(My8Jw|IavtNGJo3O?(iRmdU( zp#$h8nG?Bm4c{jhy84dM+q9oM%^R&#^*sC8odeEf%gS|}q@`@ec@wX4C?n_eb*~}7 zSYdHeWS&inNA0B84s>E_d+NtY(=IEQ)QJUhVCZpI=$motO|d1wxB<5??ki7vU_CmQ z7vROPO|hc3fKFi9NUOt6*zpCAXp|-UTc(xF^(jinzJhKJh5L5?XME39`|tP z9z{TjCg%DG%P>8>8(O2}?a@A&0O9Kv=CI{tQCycxri`0gfLzZt4FJ*a=`+ZmS;k~3 zld|E&DO1u&DO$krNNXRXK={hRZgjgEYr8Q%Vx&k#{(4dR5FY3`V(RD18`VqeO((Ia z#P*9{hcR06^;`Q=D)0-W6Z|g?M@;Ogwk0{M=X*HjE(1T`mE`G~wYT;fD91YEaKBq` zKYuhUtu%vL1M1|PqL6g-ZROl$%jk(6=-Lm9QggbQkQlclcq%H&gFsO0f%m3Dx!eMI z%phE4;S4NTK%swy4x|u`$Be<^T)UHo29CU2hnrsiE&_O~Lil0*>CJgU1Ffn|{E^^k zC+g@#n00=q(?2bK7Hro|*!h5EC8GO5*`0u7vR;a!>JI9KY#b7xMtv+5J)0$;(U$ul zvbf!`crelONhy@57ftu5X-B~oMzIK@In)*w3Ed!=-1aScZ3C<0)yH?7Kv?b2OhJ3h ztH9PU=_~&Wb-sxY2D9`WlKB~DK9(o;dCAFJX;6KelS`{9%I?Pm_gMBqZ4GpsX^o}X;f zRulo(@l&!av?4zVTN4fLig&+y>lL=!lXATQ(3gN95Y<2w6MF&NS(`I59Kt0p^p%y& zx551X2P@;5#4Kgj)J%BR!W8zQc6MYzQR#NMLlfaVI`g9UG?-y?Gd5l}y+_dltx-X5yk&Y^f8-scFw zu5k;JKUqoVT_Sm_y7$}L%4^TP-r>9vQA_JB(=(ak!f(%r_v$vq=f0fuITulYZwNMK zaB0~v08=Nh^ZzxUzOT#7J%CRg03ly!ZqEh&9{}Sz>m`ovPS|AMAKr{B$(WksueSzI zpB06I+t_b?CRKJVVA6bUB9{n?ZI_#KVjH@Z)~f6gzF6=dq64(OUd&kPu?k&wn?&JU z!9JN&vn#@ytjfbjdd?+K5bLsn3P8Y)l`m3A!vjFXOC0~`>fKJbxV$@R_-MyOwBUb7 zK~xHtxATp7cT6G?W*9H8RAdG1NwD;QK8Qa`^HDRXY0OjvEHhK(oae7Y zT5dcVz^aiha+OGm;*BlKuug2YuznbQfgWVU0`aA16nqJAD(?V>IKFCxYm9WW)gI~w;;JHNBL7GAtHPYoK_;{nVHGsy79a~~qV{d6 zge+u$3^QXm)Ybt%YO84|N>>W#n;$%Dp#1hGHKX)G`r7z_XdqP4RY9Jq|I>4IFwnd! zJu4Ue4rof{&IT0rn@6-x6>{s^uC%jfR%OvBd^(VgE|j51#eid>aud3&&?WT3?^txN zE&1_kX8mdD5)GcA+RbmIG^pn1Fg=jxAA$}p@X_5w`*Vl1?;Zv^-yKw(!WM~~;xvq^ z`6<4Db2`G?^S?=1+bdMxIDkUf?Jb(t@yc3|`=HHMlxx_i0u4V+cf<$mysg+2^2q0? zt7!4Pv5MT%!i~dEks+m)sB4Fvlnsn9c$nb9Mn74) zh(;=#cm!c;9}yrF;h1fi_KzjU-}2S|Oe*K9751y7GB=Etz~`qn>lBg;5Jm^H!H6Z2 z)RZU~e?I`{Xs;z^5Vb_|0XI}Esa zpoDPTB7CVafad$2HR8_-?wllHT9Eb0rJFqZDrwcysA3L7k(#L`O|ej>aQI=j>G3M> zA_qz%m_BnOyGc z!#i6LYo>&>8dbVFPMpG&IjBE5cAu73+T+9lXNp7!Yzyf50`n!tfPXQNS*NgZpju`Z z@z>-T)~*xopgogc*TK07!UyY;ULu_JTSc-wEC)e6yyo;u=gg|?WLOz_B!ox_Ahrv^ zaWxsTcw!<&-h#A^n*kDL8GVQ$d>)PDHb{f!h?nPG6+0ud=u@5=pkGy8m7sLa!|Eo;!vc3><_SV2rc{=&OsZdUFB^f=C z?*WlC3AfZ5iLG+(-hzuonLw#^tJvoIxwxJDhso^8;W_%u%ati}BO>2xn%I)1SK%{Y zq*JU+?aYJ7ZQa@{qhB;&vlKFEjdLPtQi`Cu%A4AXz?xO0o(R{e1{()}7&a}ol2ES{ z2+~pSn@uuPVJFZuOK?G zp&GRC<8peqP-*5kUpeR%hFT*#%ng^PvoOSH8^sh!YmhXe~x)6rc%X~QqI_j*>C=3eQBnG&_aJ{44 z_f`QX?admPQ_%A+PSR;l>L;#mPX$Dp;1fGX(wd|4eR47xaVbZ)D*y673k3=k@D!hJ_T(DA@gMy71>jyHR|qdcNvf^9KeAzCc&*$TAwV3& zm?_V5eqFIK?qrIX3`1s_dXrYZWd|4fKz8bJWnkg|?gO3_hBq0TlPiGdCLDDpi-R^& zXwOHVWXh{NwG!ve2Peokj%GDMaYO$~4cq_t20>c%Y)v0o2G=}v|i(-0= z@xUeD*%UK$d3r+zvbi62dB$tUX!xu`$m-!iE$*!0#(FWFK4&6R>E{kbKVvz?>w7C9 z{)`c}`Ox}5W{|7!fmjs)#8Vspwu zJ_A0!oOg0Iq8Ww9e;V@-8x(^YVeStzs%ghP)#bC~IvZkAoHC?Hf`*%ha4#YxMHN2! zM%OOUAU^mGqq2t-)}BI@*fH`T>OBpu7CSmAU&dwFg42(~JQ+~Xf<`3e24K28A-G+q zCr)n>o^knF;-8_!oCkmfp$mq42xmyA{<7W?=|XaoL?%=n#(>{(NT57`*e;>+fhyb% z*B)r3ww)zlDDx7&CG<$t=&goz8#et8!Vc4ksw+=q-7wtVfRR9N{Rly*^~c(>IG!Ze zQ>HTstv7vvDAsw!bx_7But@~BGU#iOp-x0ijG;e>e{ zPej_1sUpUuDn#w3;ozmvb=UG(E-a@uRe*JkMJuH6)AGbft9IW)j)OxT54HGyBr$@x ztWRDx>=pF%pr#d&5Lc2+v)FB0fV1_1a8om2wj6Mg_k>?_p79||d&k-leF6g7Fxq&p zL5ktSfg_e28UJEe2l`=9&R23=;lPbL9D!Yj%lySmF%fk3`ct#2HuQ5%pXo)Ic)65! zlyB+a0M)mnXOm|8UICjZ8uCSty!8EVlLzA2neK|Wob#l>5MWYZ%obA~WiSNigB2p& zPf1QqEQG@GAky|H53n7aLznmX94AzJ?WrGv6x-%&1K()^hgr-aK~nBdwsSTY(fb-j zN=XHC-DW?K6hkmH9kWf(^xrMLA2%c@U@+KBcD3AEq98WsNP1pqDp&vqi8zzMdWMni zapJdAz_3llo@4>d^9Ta= z@|4Rm#&!8ia`@x3hWX(bQLN(rnmLsQ@M{X6`fQ7aY8RoE(P3}B{21#NvBgCEWO@M{jZBGU*Z(nZYoucEayLrr6ff`;2Abxj!p~*BTM6cWqYO zdgb?5z*udRq2p`F7T2_KDSPsi^lIHnwRYW9whtSdF-DIX*P!E@n*bu82v`*2E%CEELyInn& z?UO*5Vl`-i&fyFfDpK1&U>ZDGL#MZ=kFFc$-#=f|o3Y4ns37K^oye-96TOSMyDxVz z2Q8)-vSPT^{yv*==d{{P8G~eQ~7f9YvV z(n159w$>nT9T1Xgf*tu*cRR5NQ}r*tTHJVcX`1DR|7$U(Oc1VX9*X%DIw%ep65`#|Y zyOzCd>v)lhfJV*z4rGGIFnY6|{kZ34zR>Ya3@(n}(tcDPy(=xLyRDj2J^|P=X<$q} zSQJ)*ck_4_>4o-i^W6ql;VDmP_PzQ{4~!>@L$d6=B>PkLO>?YnAoY5~Fgk_}#bveg z>QaMC&Uqi|FU6xplj_#nxx*|Y=11(I;aV6%#p0$JM~U{YG+d(L)!}O3Q>dtT(%_hr zXf1ikTEu8`Rfv`+vnr6U9pWs$#)5Ume?zAvZTj>D8u7WM{ukDG#;_0u@s;bUEYb9J z>=*NXnD$%Vi?W!KAGgNrU5k&CoKr-`GcuJY>4ELLW^h18w`tEbm4%yrj4Sr7PDGx4 zBLef|-DgFDB~39EEC=$r4`;rz#JqkbN~ySADq9`2Q3)zNeHe|BV1{-rYpKB{R|sqR1c4Udvy z9cTG=N2t_h^hk5WAZZq;($ho_`yRk?(e8joTwlJIj<*niV`w zu4xeej)(ssB?kTG#^o>!mY(Ln&^gH=*{MHz(`o{@P1mcG7W1AJ2LETR$6eI(vLWr^ zD+GPn{dD%tF_OAfYcJqPi<)bU!bS+~=Z1pE5dfl^u^W8YujAJ1SzyNh0`u15p6X73 zRl9~Mt0U$F0Zxz=;M{hbF@+aQiG1dF=0-u`D!U9VBxBg)dMsYKcDc8qH6Ox**}i8v zxsP(DuA^WI1tl#%Kd)N6$RH4UHV$QSWLxEAP6d#RCI+BJt>Lag*tSy6f;xNd!p*=) z*h79b-t$p=_QaaNmo~Crx1*P@;T4!r+UNeVl^hs5c=ZaRNQ3hXtCOo=c$B(}9O{#J zzm&%Vvse|O$1MvxSi3!|B-j*b+&c&YldYWGzX6oW`PXKJ;*BGIpgH`d?}U zpn_pQ)}GeuP{JJe%bo8XpAp5$svS!`a!iQpX-ob2CVYaQSAiAxjG`5$o}N>Rm%xjJ z857^p!r5BX2VZeAc5IyRiHBZung4wOxVYeiE=IlBw-tE6sgK;bGP^6t{Fz=j%Rw(T zE|ZquLgm-UX)Hfd>J%}!4#5_X<8O?4oM+Asx!I>!9T(=vYfGF+h~Oiz=j>bk!ZEX^ z)qjpUIt_2n$Sn^SqJ5}PsSO~-{oF?V8=O$)Ou8 zm^+1eJOdGB8td}hUmPko^Qz@=RX>R%@q!wlN>L@ z-i`|t7D+Q?hsIO)JVQt6M6Ro){!;Ps*Z!NfeCTjxI^LjRCow6X ze@1h)ZsCvPTig|mz$;q+)0S73yRI=8$jY^|F8;K(Yi93Klek~M{a=%Pw$!JlvCDioRF$pT5La=<^ zz4d|`-lr&~%~UMLqyEynpRXaGhQIG+1sYHU{pK8ahZd;Q<<|W}avJ#*L8txyt$`Yo z1`nCLIIux;YapDsV28AmeGH50bJ1P|T z0x`jYg~EhxlCLXEvv^iv9d4z4oS9>vhe)IIzLf@n;M4-KR@h`}=a1K}+Q2#*GISNf z@OAG9IMsQWQH1Kuu116&|4~Wu%^_$ZyMp-C2EVT^Bc?k2_a55m>VyUm^#+j#c7x7V zdqgY1%}Fgt#=A`NNd*0`%P587sIGZWDw2vol2lip3MP>}%CeUJv>nIuNYjC-MQ)B) zj_-0royGY}3{le*jgt^<$L4k?-eg&_)HS2Nv?Yrf@42fip=cP}3hV8m*{k@8XlwDB zSrzuUz!4*0!s0%|FcuGt!O6 zhsbI#rH#Mp#1~{*v)q353cHIyS-zq*kCqpi$D|)0vjW@$1t;pT2nx0r6qM<4$&EGm z7KiQxmt;mVKVDox7l5tkge9h4z3*TT#B@qI?R4zm99V0R47f-2^NiQ$G5nI~0%}}L zH|mLN?XA*U_R-Df=xiuf;VS1rbCC;n!}{uHf@T#LriQ(DZ0ehAH-t@@MI4~w25DtY zx!iAnfnKI&3M*W_CQ9H(pFQzKn7axS)(s0`D9N9k8i_j?l_mL2IU!)HI`-RPl65 z6uOPxDhw4e}NOy(w)TL=|9HT z^y$_dY@M|~=@Q_4kfW513I5%@At^;p1^MyM^Qb%ULDnkpylM%L(|Z7a#BdZjPde*l z@7eog&CeQU-L38V&vqyPhG*L1({?URa!%lNF`gP~0;?~zcE=A(2XV`0{S9#xf3+yf zh%s3`Qyd!ZN>(2PFv*v=Wiokq`pM>1d_{G1Z)5{KYs>j~`C>(z|X zQ2AY%&y`pDLQU#vHgE(iT;#mIpEvc5nCsm1(8L7r2uKKan&6P=SwjpdEviWV_l{ok_7-(0u5<~G%ai*7SwGva9F)4 zMb?dcJay5WvDQpGwYIlCWpM=Wi9-1bk<(|ZI-J!Aqlgoj&`&f7E9?zBUfBejSRplC zL3-?$B7JEP0;uK-loI%ymE^Q>SDHevOx`%-hWmoq%l_*7nnix_(3(nsZWLc^{>~1l}4{?@U%WK6y_3#JVNDs7Uprw{=qu@Zm0SMz)`3N4Tb(4Jdu&d3-ch ze%@k`_x}}H=jGrqVz$BVSQUTOXx6_V(#zCRPP*g=+@!Z`q8hSH%n1y zmtF`UkhldW<_5Fjgsms3W2?`VE7@wHd^&!)L}?6rvIxG?8<296E0=F~2`>C#;R$M=y5#mB&O%}@IEKfT>{aCFSET1{owY-K0vv;Z@G}N-7#8(IknJw0?*VK+zAQ3y=tV zH**nRa}45n@4H^#Ly0gG2GwM02C7{50eeMYlgGZX`Z1}@q(joxy)4q6p&4j3=hr4T z_xTYm{zh9HDT63F25>XCVowlaKtMctC3G_Azw$eWMk!N2j!>h5e_^rys?ne~hR7N? zu0{_&u_}?<$&FjI%APpXCbB&a+<=u0DH%?Q>zrf_n4@gYcI%GxBv+{2aGTVOk?G3q z1JwKN-TnAs)%0t|YXX_;L9k=jB_#l`V4n+t1h?rxjuh7iNl7_pw4bjZez@ zvh;P_7{T@FCZ87LPOW{kE&&fgcbc<7)~Q*8c*QpXJ8kKgH?h>H*^=v_0M5Ms7afqwsvg4*%+tR&|XFV z17++HjsJeu(j3Q%V({}p5o}HLBF5{}V)t;3>-4-B!ag_x zvFJTGZqsk|U^ucC5laRD0tdO(v^pGY9TXJ8X|&d0M@Xe7B9|6#UgkX{6^C z)RGhG=5xLdC5z*yBiuJYAe%TJ2JE&aq%qI!+DYZ6chBPKtkFnX+JQ@<3l3}-UnO4n z75VFK-DRoagNL~o^v+?H9#QN5Rl!~NQq`8_vu#Tp(;&ZM5>>r=c?u=CI5e;Psh~+h zV0bUVa+$u;oHPHCIME-_i25^`8i}z_$9sAnbI*g;q$Ny`%Wf3(xWSFcj=uK?sJ+Gx z&Lxp2+{xfpft?U3%hGLSg7Mtgx0$e*4tq&-Rl#YLXXL01y2TeF6Q91aIF9$`rkIg* z>5@QXS)vOONYJA)-uJ#}s022!{}`Xg;pSC9^L7E|XdYM64`=obq;|E*};NLn>)!c4GukpG3(~GRWeJ+C9k-LtzJB-j&Tgrc2trA%) zjVjklUX#aywvW2$i{l@)O9muTFXp~%K>D(oai3nv&y7l7M($bNPLe-JFG`G`uv8fM z-;yyf2TZmXa64t%dLI`5laksoN6Mb@QmCnE32?JZVf7Bu&ZRj1Zf{!hR!w~{eXn$- ztNdn)%}7av#4Nf{@Jf0V-*^s*6GWdU!Af+(!bbEhI*%OUtL1 zo~*RA+wioBp`^x$u`5a=M?4Rxof`#$^)iBR!h5!S&goV?6U?P-^4h{jQ;pZ2BKbaa zl&-2tAW$vEAd~uaZ%083Zu~E&z7+2E3bTk$XX`kvu1q;W_A8_JyrR)cgjlBP+s7|C zL_{1p9WYRj9>Y_3_++T)TTb|_P8qHbn@x5|zGQk_-_9L_(b>GX#T)L=MJRHA@=Nge zvYwG{W!u3Mbx}G9DPXM4Z#<3xbn++$_B?UCAfj@3FjImE)`PJ1Zpz{gdKQ2DcEMPK zKm3jwuE-CWr6EsMB4w#X{n8~3#SI1iQ?CVFVH@9vqumJ`z6yIIV(Ad%VeBajr0|VU zlWF2N@!vJit*mP33TRdX&AHw#qFW7)zqZK&&4$)2Vz9^F94kXx3!M^0(x@d!B)UL5gVl3 zFVq#)T?u&Yo4_0A*0aUTUA>S8edD`mrg({DB6@GK|3rhz+5f7N{B5Xs)XO-3Dt)Cf zLO2LRu6gx5$i6&`P0nxg5#^-ah0A|E(&!|E556@_zb)U_mk#|Qf$ulEa?J^Q95{cG zYa21VVl)eRoN1hq(Vv5M`E&u8j`A(f+gQDU-gsG0y=jC_xnr0Ef-M9?=p@9YPu8~E{Q6FybPzSPc{oKu~G`YaE5hG#`ve&0xT zKqNTV&NK@b(GS9pt0Ik=%2At3gT*_kU|+QU_%?yV<4y9{-nKo`?7uc}EBI^6i*X%@ zs+@)J=86SPCw{0W(k38Hp*KCIV-X1=wG5UNQ(#yT0HCvy3AW{?`T$2QEGsj^402-Y zrN)2~E6E^U5cOUILKq7jmH*%?0y|GNojc23mW<^%7HnrbXL>=&^@!L0_-+u4W5K(> zBT9CsrZsQDmxZh^+b9<5JpUpl$Uv`y-;RoG2&)HJ!V3+FMYhNy{ElGMIn%vl=i~D; z)S12wgFL^*$PozVW*w(&D~d5y)21i|e##M?F%!iJOokTv`DnZ0X6f0$8-;HHxIkr` z(3bc1az7Bw?_~3sw-(5$oIXn%qa_!bI&7}fY6e5tVCe%2aX6`|Hj|%T52Bz1J{(M9 z=UCDRj+YFHtI@6x)SSNe?@g4d8b6Zm;G~1j@Q!N^kiJR5sDB4Kqius5D|^p=j_fzf zq7csZ4Ezxeig1-jRO!3xC4PyEvqrr)1gFa8abr8CPkC7IS<$Fn5$Mb-i5|qlT%zZ8u?y;&wqgJPgj3!Sl>jsZ#43J|=t`nCI$PzFiyNCu4X)nQI= z(mQm1KZYr91zNQ!F{ISAIqrPJ@4+xuW7zO}(-hZJ%EsYCaif^;ySfIxR`i>z^47V6 zEBNb|XM}>vs7C?}sBr|}r48wBYHS;L#{+&H2nG03$18!ofi(7$Ps4FjsYNadA;n+? zdq|BSBh695Ex|~%N!>nZvqWXBX35$htv%0@6L_cvI4f%e?PcMQ&XAUGgOt#z8fLui zGgTjm!)wlxodI5_cWI9gBvm9s2z%C^1%KwXaK9fv1BL&YYrj*IyDbZdwEFJY{F|2d z5!CBu4WkqHn+cB9!P|EWoe?-@Vqo+MB}){qEn^hqIBn9{sDAy!nJ;0-Jt(E{q~?ZXLJj0KpOq~V%EBAie6^X z+uGF_UCSsR#Iv}^aDg0+>J9^f(Tlo%US33NYro^$`#+XK1Vovgc>H&e!Q$&lk&O_0 z2l;A3s=9=tx}9EJVM)|}M?*ZF(wXBFmaCCFf<2?0JO_c^dH;(-_J(z4KvE?GEYtqnKL_37&q~i{|g( zW8?{`;Rk4*RYq!+{+Na~!e%|&4ytW@6;^CGc{bnD#GE!)>h(;4$?;0ehX`Vc+o&q) z?$V~pw`4Sqtm=ROsotyZ_ebPCu0V{&^0VW&{z7Cy>A{4sjc1Xt5eSt}{KV(Z^09-? zwmKa5H9D8zPiXGQe1gQc%zzFA1PatWepig&yPBmq7VykT2VI35>wM>yx^Ifb7vPx9 zML&z^KSfh22E7^S&zyo(>DhW1 z>U+sN3~x0&2`r;H^uQS}z1r|sbF6TQqG=ptNjNi8T za91+@D>12!C<5LxIW-uX8|1&hn(WGply$#p%ORA=MbS8+rh2R&;nD`(&D?cKB$yVt zbPJc{DBQ^wkZ0R850%saz|!V8>FZ&r!gs^%a(OEyi%*igAhB5!`-P*wsOaupA!5|! zpG*nhL+^qVz{%ZN4zF)FvrKv;2Q_a%Iw>*W&3Z658ElkU&8|a;UeEC~w}J=!cFtof zUT9YcQEk8}^XpSCFJctxElgtsBKippM6mmENG>H0BK7w41RBH*6AH*5AlZlQw7*km z!EA*KEJ!Fd`7TMy#>2PKaBpv z=H1Wo-r`@o*UT4HatuoH=-orniZl(IUwvb!G8-ek6Zf&ovqhV~3PJ-*e#<*E;N^MeAel)`TZ9ZqUBA8YMu?sf$uuBk%g zEH+so^1iW&%@E?Xt=2*n_431#D8czQNhdu(03@}- z1wl;-)D8xz=&;u;)mH137c2=+7LjbyN4pqDFF?$-Q@&E0ST0nNIOPyBq*meh3GWP@d@rCU&b_%ZYpK z$C*UDmVm9VjFn-G7%GQUXISc#QiI-N=WSA6LI^=`DG{M~*G*LSafnw6$ulK}n0!wm zm}__h!fiy@tw*QhaTVFXstAyjjel<;h`S@V32NR~km)Jc7lBZBo($G@bQ1(!a*#q2 z4<2}6KH_43Sv%vpS2r7oQs=z(t7c3w3B@G0MF@KhDdT-1D-Y}pZ`(z_@y)@5>Nyi* zbodaL>`hX88oRQ+`Ftmm%seuvY|eBpej^9foLg(^Z=RoedwS|Dv$riDESEV9(1|CYHn_fe42ZSF~-^-U|Keki_(l6x!Qh* zM5_L`q83X)l@LlnNzX`CK(wp{iZV56I1q0S5%&h~W}+~lRjybd9@dXmYCisBS=6#c&v4|;n&G8j!J$GR_hEkBeFzhCYp)yQJ6Hdb z-!RoRf0sTtO=vQBc!_P3PSJ;16F|x{i}>MmMHJHLvl(ife*1}vR5(YCPRGV0y4@iJ zdno_Ea zY25Z01b33rVz;DJ5LeS`ocB8W?&y?Ig|@h!F`(Q9b01azoPuq_;n1<) zYMK!>c2UGR1b9Y2ksZqiQ0wwS$n&U=(ND#&gvc~-Dxx$(*co;#xqgEruw3CIV6u=k z7BYnZ;tuMeF7f2WS$D5%60n*|0cL?7g~Rg$LKO%^kY5~45JZP7xN#Vg_uOb_4k-X> zoHMXZch@<>_Uwy1i}_$Z9jhB&!n(d<&UY+G(8%U&>db|F!1iELhUhmGOm#rVVR3HzV^Tr~QeRS2mwJVC=>{datIq1~>L!Q*HZTFBSjnn!3;+P- zUys{FPHO6m9WK51@SOOzQ@S3xCHt`l!7GZ-yyI(E%0#aOpWMs#tEbKIT%8! zb-5eh4v$aOGEgavJ9xBIh4FVABSfax+bH7TI0W2sC?(m7n~DPhW97uPYJmWn-(Arz zc`AB53ROW^ZVx|n``Y!FO=U-okqv<0P_(nJ<*@by?J~_IuO*dna0&zSaXX>1mR{p- z6x*jE_lSjhgUloy-^d>uH8;0r>}wOl_I537wl|)$R$V+qIoBb@Y{T`<^AYh)_{P zDi}5CEa9&^Uge(C(>d*DlGaKjEW2Y|28#UDkZ=1GujRV;W-DZf9(2P#10qEzNU zqV@q_O&tVgk%{lKw~=*;L~Y49{0fOSFHc9dw(diRbOC-kGPWyj>+6S*`t-#$&M()y$8@~uZr&$CiVLgA(D0th$%JNWM zGQmyonl)S!L8#M-^IoFHycXOdYzOo%2h#g+(#ZgSib~3_X~?8~*`4Dm(949733Iy6 zJ#o(uMVQN4lrAyr)Znvb$@iNnlvp?1 z1~pb-X9b8&*eOFA-I|0&r_V&Yge%d?V=y{(;1hZKAOqrj?{G+t#iI#8M3++t48mC$ zHJq*e<;Qm44yCW?#lVA>duju)!h*+&=J;jAS zHB+w>@XKh6vU-y=doB{9yRwnwhgJ}c*YOlHa&6mbQCwS$_%+T$g6qy81b5fX#o18m z!R_vv-ga#~v*;QD|2W;n#Fb4F>a%ag;51ec6A(@*R}7^46Gt0RSnm=N3nagi1q!*u z%vk9T8hu~rq4^o7xNdrGHM3%;R>(Ot8?xVPYI#dMCK}0IHdit8c-bqW=&z4b)s#6E zisVjjuL7l;Kis9=04kHD(sD>d%y&SlSb6qAELyFQ#VrZ=j01Y;-Fr5isA=gU_85OT z@H3A3;>q5{tIP(gh03L*t}tN1o;<}kAI(gD=l(-i<=9gohdNJ~-0WEZzE#kCeY=@0 z#S#c1VYb<5poqK+;VMp8W$(A?#AdTZ(M|tU+>j@Fq=}>>;{)C0Odohu>l(3rlqZ)x z!)yY%fL(kAx9~)w5=X?;|6{ro5RxqDAEyfNz;JTY$9+0N9HbulxEAp0%}# z8$Grx{80{+rCK_ih9w182`lC8j04tu;58qgH~o2 zduT2qOJ`J)Z?WnP95Z|!@D1c|;Zv`et>pvDDRG#Zr|l^YqkIaKm~8tzavnPJOL|T% zL(p63FYPgT`Qr;HO3J&v$`k70iNC3-t3fAg3J967hhp!Ho+pg6i$A+vnZe%gTksi^ zN|yhxjiKh#oe*tAq{|pUG8K;L$BGf=Dr>}t2%W<0)ckEKCPk}X$|nr)HxuJ#w8vEUBB`OOj}P$60$HV%RT7y4_?Y{cZG#&NQ`nT z)B1EpKl%aEKY)&_D{rc-3ZX+(dBEzXV;eJD_N7hVY*X>Sg8?yZ`$%`ac6sLtZ}ZCE zbB`>IyL+kB$&B7=_N#ASWT|M{Q|F$z&q$6AgH68zxzjNkxmdLfT_!pcI($*D z3BB#-KRl>=R8p-HH`V8E)mZEG-X9mnV#dsfHq_QOC+=_aC&fB2q;u4|eSaKXU^NXd zs{SVk`;e$uD;!y$1WAc+SXJ`j>E8wLEU_A);27(Aen#q|0gK2?_b)xT;6y6|RAv z=tWk@y*h$qsA%B1LConr`(Sj<4WR0`b{T1p*ngUjCIijjp^HXighuyi^8G!n62`*%*Aeit#@T*U17I)q!af)|hhS3IZoa%^1w;FD3K4eXsbZt%eEp9yPjwM*o#h?-Y1$+PDL5-!^+9zGRMi72r1o`TX zyCAbE%jr@$v_0*&F&0SfpnCK)UlZRpAu4AiRsjP^FzL5rhAHZAyp6%}A~pbT2KE%? zb<^kSM?Q0osdjup2Ey22%C1`yBmZxOG}*?wx9~dQMtX~ zUT3(?L}T{g;jID+qFbZeb0h-Y?_% zVn&iEk*dn*3^L7n%0Y7~7+t#aNvrGYkttc*%;rx@MTq0jJQw)9A6aGRHlHPKNDr3S z+n*-0q`Hu+?iR8yIm>E@+I9%!Tuhj9z>(hIPwoamu5yEMd=&BUoDsIds>>16@#v9X z1VZb#_20+H%VeAL9g#M(ChQ4<4 zgTJ2IFsd~)3lpa8p@oV9!ZHb<{0at*^hX{fS(8NrHZfGPezkeXM|sF$M)y2yQxQ8#=>B;53>L6X*0Hy7w4JP7L8EE=>@dA2u^wo*+S3LCTJHQmYpqm&~u8Nl>$@Y1@B^ ze-7si7rNKsF|$tj$^9-XHphB;&Qo42`Hj8=XSE|q4*QE(JNAK47SEiIoIDJ2%zq$u zmzVNBA-^nbg6oz7%8jL!HFr$V=0d`HSJNG8^pNM1>dXFJa&xYRS~kE`ltgM#5EHrd z9ZBW6`*h)^Rr4`(anL)ePY%FtuDkmUt0v8mufMzo137|tmxPJS8+FJgkzAWL{K(IX zZM`~siKC_C=6b5hA-Y$*u?@C2g|-RpkU7ne9x61*rLW(`^xXFx*ewNYE0mm#pwmec-=SU z!(Y=dyguD@=aT>IavtwsrRXl{yEz{m+CC_Fn;B1(fLk3Yg|74Vm$QM-rsIKMIV`cW ztcV5l#fc6z;CjtQixyi?C=u*A)$R4k0MC8-ffC>L#J}B0sqiVRFdw1?!=Ub$YCgR= zO9;4Gb|G-I#Qpe9$h3v~q}MiWq9bdnSyeg)BmNFPyzf#btDNN%oRVIfceU3=Z-)T; z+(E5Y8|X~NUddfZSSz^OHCZFrS#l^TUyT5?RgNoQB|EV|(C^RxbKz7$=uSy*Tz8J4 zGQh%{5S}FA2Y^-KEkB{(VBizc#9O;0AhJd)F<146d|;oz+T>K?wG8uT6*z^NdONqr zS83}>AL;X7iH3xX^VFdRsf@;TYbD_b;?AUu!iUX^k|uG>&!0dq%sMt~by?Jzm5FR# z=?UtIDCp@$BNWmb@q%)*^ygorvIheLo1t+d@msT}N7s+?@n+6iM4z{LA*V^dh;9T| zS@JGO7rqw~z08;~q9nOdL$S6=9<>Ce&^zLa7+UXf2xS!Q zhRK#ZFs`^qY@*Sj3o{|L!E}i%ZJpl)H1H)$tsF3yXKMi0up}+=3kLh=szxeKGUl1j zCd*=_Nr{taW3Wfk0>PBbZ!En~&Gir$davW^pTK+^wy7Szylf05MRoXuja19yne!)J zOA7jAV(1(y$2C$&ch})=x!uaSe*n=BT)ID7 zJ&LW`v)j|tJ=4?M^Xr~)1$hZ%1bhSl0DvqdDXIhjd`SL#!NC9k5W7hHN8lImc9NQo z001M!-wVR6P|yVcfbO$U(Qwj`ljSkCwPrLlu{8oQx>?(SsQ~~!K{q=?V=IsosS(J` z!iJyxqOFUZ)WU?HT%BExS?$N=-lt)Qa{9pZnfAN!>J2~0$ zFfqBhx-z=5GTJ(rF#)-`x&QKDVPODMFgUu~I2pPz*f>)Bn}aCG(b&Pl&dI{ohV(B- zLnB*fCw_9UwEtAY+U`GWZ5;nKRNz5lax=7J0x~lHRqG#wCdU7vvvYR%_K(g@jF~{+ zK-M4|Cr2_P3EKbC1SD$c1mY+EJ8ulkYz!LmM*?*l9A6|5bH#yk1 z{k{EYMkoZuHR~SH0@efwUg^N zz0P+fSAL{rrkQP}nPr74NE^=b8H_RdMY}9TFGu|(sKe_Xb~k^Dyg1fHVF-s%pxouQ zy{(fAxn=Sp`@Q)i!9)PjPgqX`(y#65(XAKoN8d*TX87AEDI=K?yNOy#uIC&cZdek*HCvv>nkYs5Egrk(9r+z0eS1=kkugkR zPD*^5eEM0$fxXQ6*UE*|hy!YNCA^@0%t}WZKix9cMYwXdB1+Fz*#RLs&x~bgU>k5V zG#Fpe-074y5+U`4(e#X!Tk?d}DpyW9wNa2=geA~JVYJ(FwSm@e(<n#xfc$dmgas!*Q_FsMVoI_6b{9=R8A?Urfb)hDF$?r}mS&<>s~WFH+>f5D%o8 zg_bdT(WO!p@|hPfSOjlxm&Ae*2q$lbXT^GB?fRNTkN6gY^6T7A$MZiUxL|+U)a(qn zekse=1T4_WeZtg2_1>S#A!A*wNoC=55`p}+H9Y|n0U3(X?2r}9!zApNc}l=Bme_Ls zcci_9o_O>3h<5ZeS^`XQ&mQtfuOK^gTZcMb-n?uI8HZBsb zHLAnpv3(f-R0e(wybyhCCtGVSIB%vHDy2wP!K+SrHeib`eZHil`-YJ>4O3pBu`G~( znf&xBgQR+NozLKndrU)ILdZwa1wpE;DUYsNOk$vtDI1l44Ch)KtyWgHGTlVU* z1!lI=O zBn;mL@i8SnCYdM&VVD8YO zUD;Y|TWsUDHGhaezbm%==BS|L$~N<3tZNsqzI$irn?iw^GbPM>JW<3RLpg!!VpArJj;Ld0Qtk0iH28qqm*Za(dd2jS9 zrt63D5q`Zy&-PUI0)qCpAuN^>N2^57my(;OKjjkC#qF1G$ve!kI3Y~_;?dlX0?toT z#WLc9QI|v4kpm1Ruh(JcXFvZe6^dsI`s{OX-JSnROvZg?^mE036%KmYbTf5XTNgtP z9!1xS2l}dfRggwMtWs1-44NckNp9g1Exj5SWQw?XnAb-35j4|qP4vXCV)K2JJj(r9 zFcZ1Q1tO{&S+tAwVC1}MT-)FJ`L1IVxOqP@vE=gg^hdZ0qt#PT&5`J5tY={c`~Po z;@Qd`i5wN4Z6vB5FVQPQ2iQp;0D4a*)NhxCr6Vly{wHc&zQR8S_&jx5YG1y7zJDr0 zIwHI>X>(Qc$jt5%bj7SXNC}ZOTkd2nPTcEy*O|B=#$Dw!F^(Xww2%c1Nk#;(qPKQ# zthqhlQ9i;>3l*po0|3qTnvdboOkop`6rLOpuYzhM=2NKvyN4M$q@%eqLp+30kI%!| zAl#eNxaF;T{<@hz(=iFiK3&^Gl=adU=8U(;=d;zc!-8Q{|PyDO-~!iAAslEFw}^`2upREg@G5DM>#wKnT8U zQmGzlWdG*5rIz*&!lmXQw${;9{E=B)O*U~Rl(FhBp{JR*@*u{#{;j?k*|G~KV(_g# zQM@hA!HypLrNhu@l~1x!cA1|}8gbL+C=Qgl%Mf?) zLV|I%iFT*(+%8=d=*`&XKYVqa-3CQ_-}Bm5k@rtlmeP*Cz>d#XbLNS?httmlK5$jiEj|X6h_1yGP*rRU6lF z? z_2Ax2NRNv}aPp5}Bn7!8d)OgvJx>1zhv?m^08i&8swv7b-Kl#e!AC$Xk3(&Cti3}S zz7k4VsHT7r@Hsa%{S+VuO^$M*X!_i6(*irx-)=SmC8FnM@h~H$oMYpgFoO!C(*v@%y81PXnfK>C6BRBj&OB9+U-&9os%6W z%M7X_NSU~du&INf3t5)zXnwbYHQL=r{tPvP-L3g*&)Xc(>rn~n0m2rqy6y|*T@fa6Pf&6G zNOfknL-v;e++$P%>IF`MNy+Kq7^PrX10x9A`v^a6bo1NFf#e~)rZ{{de$zWrM z7~rRY6Pn6QB0a58&hVkqCjRz!b!HGM9>FUm)w;FcUd6yHhEeZZCgj8K6ZiJcbAzSB zoppKTuOpe$`b^fClx?`{1lN1HLyp=m9!g7HzR%;%U@y>5LYy6e>dMaOb?A6;6W;2F z@8IMsq!eTRt1{#0_ALTG!I1Qs@%>gNK4n?TKDUhx`f`c{n_}R&E-({cU#CK!Gsi3>g z-i8`QZw$IO4u+)g_j+EezpD2h9d@D%LO)`TB($-n!K}1blp|?< zfKQgh>fA&6+bGG~fuFxTf)Cru)arhyxryk>Xms@vIQtVnU$fxz zl_0x%mT{;fj!ZE!po0SoGBWWPO|RV&?idQF%jZ3&aL&rh9_8iB$6FcxGL#clCcnnu z%ahzb-cJxex0*95@D38|DyDHk(r{wMZMf77LbGoK7Xg8RNrwHXp7d|r6Noz(VLBmE`um*+)O z7wyK2wf@I^j~(g@n=gwTr8H%iTTJ?y;i|BcyIcKyi)jSV$O!1|-V0?!kv`FG2o#?j z@?9@RgW11;QfaWivrM-G_X#P&18&w@DT(c(xmw@XcXYJ_-}P0_yQ6!Q2)jH8kjR#N zJGTpetav^A7Ipwu(_TBnV_mN~*o2aaai-vI@9cyT_7fBY z>dwCG6Zf5lpA4v+gp~^H7PXu_p@jtm;i~@$w|YeO$ksqddM6LE2{N)aMC8|7~@nt7IoQAGi+g%!8 zNoEc|*_dB0Qke&u>?~cTnZ_ z5!z`;whGv&cextNl;D9uQuylz8o5{Zx5lBBollE>Fpje7w-+YyyMq?anGfk~RAhd| z2x0Lp7S)sUXa^6`7NS9xlpQpX+5&B>CBybM67q)JED5{99-|2T<9P_52HUnx@X&;L z=HFItf^Fcuj`;T;@RVT7y3A)LqV zWs9P{8j0X%`##wUbBq=+_+Hx{lM3DyBsa{L=ucQ%c}270$t+ zw+vK8utiMYAYSx>_!NN&_GM%dGhWKRN`jtI5)<=NnH6#_Hwa;dyP3#zdrL{$`hqU| z3ya2?F|Ppeg?IU=(Uuq9cTG-C6^~N9>BwZ#xrP#q2TP0m`R?PB#cq3IZuPtwmLN|0 zD)IAi3&0W7mIE7Q7oyj9;dH{D zlXcM>I!2Q93qd4R33{!%%Jf12bfOya@OeqJ4;Srn6x!NKZoy5m)s!)8eM`CfW5#~K zoT%!ebXy)H@(8U?OzAZ7{J{VgD720zGgVY~(6a2yHH<3y)1nF+%7q&`!l~NXLJ;5q z$B$aINI3zYZ2FjnKzrD<-jvxu^oT!yQ>G?A9`0(ojEenyy6pR`k>6ALitrp2e&m&c zcweHlcE$jB=bK?OTqjZ9h>Rm21o#1>6M&0%s>R<5fN?I_zls*h7hkXMiPUb{UVQ$c z#>U6u*dJF?P`1e|F|=e-uk{B#1_Smr;yiWEn4y4Y_LF%Qv4jDX5Q1MA)&4e@`&4WK zY+=9Hy$|6G3vLDvp%6&y2$-rgOhd0#0_XI5B3R`PmVk{c{7aW1%33r1)(Ny;xlBWP zHTMnABz=g=Qa<2>giRcJR7!D!DHHxZBb}nTszaF5H10|X-LuTr+F~lM)%xsagAz3e z;#sLN{+Vt%rx4^#VIp$a$}GK~yX=J@Ayvojbe+q;NZR2Uvw??K*+mWr!Otj8Ljw>+ zkBlLK_Q)ie@EFN6Y0XQ9r=34pBC)cMmj2AiQm0HX+~L*i=3#ocN`I185BR8$H2(lN z>I{eoN}BM3^J7ZWg@Z=Z$RUHUVy6PUpepT)I;EGYwi#9fxZJ{y;~ccb`|^`tgx?i1#s83}dE4ycH6ajcK@Go}`6} zP<4KCG=gdq(;OxGIF!fur(nrTC&+vx`jMtUd}?G{SE2R?z1%Gvy-{Io=0N~&QEr5q zxE(Hn1*7T_-vnQ}ma+J^KE(uGLt{gjz+TZXd`#s`^`#OCwfs_~x+b!Gvf?qvQ4~wC z>m~B9bhv6jP_rF7kE3H{oe5MvY{61(8!PvcGZJWUFjfNOijdC*qLw(e6ZEgkB5c)P zW!&^fMB_DJj%k=2o%!j-jieZw6gm77E?GMrW%x210AdOeJH){W9 zrM+ipc=|D|G8lBATt|=qNu!?dE6#r92#KxPTn3`Oy7SWrupG!R`-A-eUGf4n^0t07 z;Dv|Jxf6`B;+FSm<>3zgqaS?cdhw zcRVjL8Ao{vcvOfFr6`?M>M#wgGl)z`_?9t5Q+>A?8!sPt7DI`9fAaxG=cs1N7$SXo z@vBrsGCeTYRHz@Kx%o3@!XoVMsYv*eB33CM#73VdW(y93m8+q@^2hwX-Iae^7hkLI zDIWr@4D4^fY=4ABawX44o;_NPN}FNciG!r^QvwWIek_ev>#Z==Ms7i7F;B#hS6^3K zs?Rg*4WgL4*7f8q2ubvZdAs{gI=>3n01t6OzzT~$@gd_bcHHs9 zJ6c+G&}N!}4>oE_)S?Cze=a}WQ0;nHCw%uMyl#C-ZBoMH^3JKAjYA@YBA*@6Lc0_m z^Or9KU+&(hG^O%&$R5^0=u8+Qnc{;Q{^sRF#LRaZ3t8Rd#r0=ScEseix$HJ}a!ZN$ zX^tYF=YoThQdJ3-ybPFpi?N=Ph>w&h_~ACIn3wL(r{Rin0@G}!T8Yr%5$27U&Yi`% zC^Dt*&XwfE?84z|u=^2zg3TSMp_6EnX%0nX!Vjg4RXJc}uq>{dgMrf2QbyUdQyVltg0PME^kkcpnd3?6EJQFI(%Pc+DN$E6*HzDaZ_3F5R*498j+He(urr} zZ;5lEx8^vg?wDo6H zJBX^k)&5ZpsORNo)I`$Wp?LdU#xlXRq$2m3Fognchzm@A2S0fFJbZ3%v%OyAu_etA710CtOcG-?n_aV_TH~{ zm337+TW6L&^Af=_2F1u+4?XyB*$Gs?RjY==D6XffUGnWZ1o=eUSkKz+tmqb}|buYC8CC=K5?1Ss$T3j3%zbYot0VdNTE_+h5gsj@-Ple4p-JJGkWe^Qd3orb%9 zR?z+4v`tdkB47>8H&X-e;#Rd|N?CTTu%pw|=b9B?qI+0$I(4Jwf^Z9{`*yYpVH}ez zJ+B@N!)?+*tpyq0Y(zVnP?@ zQXW^A-+dnGa-~4tGttZ#WeC!b!!{x&iG8;g0HcS5)il|XB{pNdo-eq3T7L=&03%7i zzdV_kMO|z#T1%X5s(QR~dFKO~r0Gui2zH%vyv09OtyQ(TMa9}v(u}{0e;F>W3vub{ zy1T#?{^>SpVRw7*s~MNN^S0XA&1fZur_1&mIu3H#eVQ2b$=Q$j0UIAs|Hc_!cnzG- zN+I>mOyz+36R|HKu(ke6tjjTb_9oM^Y7yJ}2b?QxP%-t+IW_#@HGb@jj)q?FB-385 zvxC?8;p)4dRp$%QD~hYWhGpYRN25o}3*yVB)2N>5QuX`Kn=bS&d*wYv+3&g=LqV8jq`kGjrM+vp1 z0fXp!A5MjBn4XzVD7ZBk<9tOhpe?2v?1j*ux9O{O2Zkbjlb_oYjEOaU_wr0RLpGKj zzWt$YlebBJ_G(|ncd4B`>B}{v z5I4=O*b3ZRqs@Ma=2V*v9k`W&oI2urnRJF_U(7tNWnb1BjY*1_v1nft$`@&^e>}!e zhJ8P|svN*UyYMm-aJPN7|Hkc8IF0Z)YW8du67EMDpoBm@xbsRx-E(l^1i5P#qW%IK z``nY+;ZuJtYH|7Q{-rNB(mlOGqc`_CNuX!Qc1TswJGFqa?@iif<-*(6zd|lL5VW;N zeyT>bej2kB*AVY{GguRKjtp;3nkF*975|;_7Cz>K!1L6HP}ZmD-R$ht_%lTN5%h3M zK^r&h(xBh+8RUUgw2mU#gW^V0G}JRJg&ts>W?o|Cq>>|ovQEwuoJ4H30*B#l#HsGS zQF*;5b3v&Y(zXEo)Fu6eYgQQN?#dkmur~`3^s*>BF%Bc9i~5=GzPZN+^RI?bU)($9 z!N3_+G0PDv&C95S^3PabJHb-#X2N3wZrTZCVYq@1hPS71lvGT&7Gu)Y9P-rNQ#IQS zAWM7|MPn2q>aX6MPsHwbT%k<++e7<5oCTtZp!vJ(2<7#4W zXf5ailZomivp7K)W6|ihOFJ=1f_eP|5*zmxCUpGh*~WdVH-1=Sx!R{2RNSYS-GLZ zoVllHS)qsjoc|mI9Xe8I1#|d0;$rYzANdGAp-hitdYR|hBq0{pTaWc=a7slW@;iZ1 zIMjiA8~y9+5@JpBOhS??%@DGO_3yfhht=1G+4wg5aJ2-=$B$KcYPpGiaYG^@YvVUH zpPL9?Y(~RBlVzW6Fd`p1AQDu#_&)w|p_3TJKoFi|G7w(pE{)xk*>;k!p)EXYi+I{U zNXiQ1=fSTWa(!>hTHt`|)YO5HNDUzi0xwyw@nT)e0D5O#;}U*C*u67W*NS_w_JEQB zqUfD3roAjo3k)`HG-vlSC5U?lc%S;3(GFbmR@+_g=hCp~`d=?xRy_Y)?{z%Rc@T9UI~2F=*;Z`b}xeig99w8pC12@t;> zdNNUot@ujUzU}?nTIU=ZBe>zQl~OM;C4@vDnpko?4h)=3fwJ+Wx|-b!n-t+63^9^t|_Tw*mY1Ra506iUkk1#`kE|f_@piAhhyoim3Cn*DXK!%I!mFzCZU# zknq!Kfp@ao1@*N$eH#pKKW(hY=lzMl;{j#=+~8~`tY?bLyO8Ob#^K@_W>{`Pqa%s@ z8q%n9wf!MIaDVKFl15vynVKeL565vZD&tDQO9B5QRyXFsr@KEoW7}!(6V49C{=C0V zIU+#geFuk}s%)8=*Sb^NMFi572!F)i7*7+K+sfSaBU+yF^qYD2(oj<0kk!yJ;#w`i z$FR3X_fKpVV~I}U3p0F}tn3j|S34pYynH|qsovuV+w$h))0p8=W@gN!o||X`J`H<+peXoDFar!$vDQ2rOWrYz_86U9e33M zg_*o%l#Tb?sZQ}Q7ix6SN%GM7x2JAI4id!5Cqz(8n!b|%DRd!OmYRHM)yeCz#OXT? z=0Q^sOa<4J+Y6^4lR@`T7Qk=a%r>Yab#<<0>_=%DXSV*_1G-k+#haP8q*TL4%SDuA z3t4t1d-kqRzc*boTVf2?SGT)?-aP{MyRICxOhGUux&X~0;JjUN`=9xvjXG}07*~I# z2(0HxBbCW-A7rqJia4oyhj`zn6P|3eEqyL-IdQcRNBNFYX;+(a=y9>poZ&SviOL8Z zds#qaG?tF1A+6~ewfD07W=wO=&Ua0!*%Wh9_=T8B8vrGYljKL+a>31fws+@j4^>+R zo3e@zLVCto{+Q#cobtpd44Shztpsj>)w<=-GLI~3B6R3>K~+pF z{v7@FQ)9f4=nMgDC`lY~-4V4M?h#2-G4 zG$MV%SRAX@A_ieF;x|zPIEmUtBAZZG`wj1|@B8p@RCz0+dyJT+D4%f#IVh$MYZE7sy3I`(}sFHQNX8k{JDlc_+hywAnWQ8b}(02F8K=n+?kR1hBIP`0;Upg zOmT>)?xJ+xmlT*=j*q7Wv^f@b-0)(~6;9tM09Ou{wu0fkU$FBQ@1HwBX2w6e5XUt1&l^Q$!62p6z?N_4`#20ySnW3($Zqg~TLz$$ZfHkyJFZwLOo^!4%T_djM%Cq-TVNan6+1 z*qm`!_Ong>22($&@ zSrOJoiCQ5G{&#?zj!(paQ3JHXC%!m3x6gjzxx^a!{3a6`M=&J&+fEGh{23hL%2s2f z31#orPPItRc0`|KMNA=S6m;kkETntC(bTV0WS7bp4|9B)a-ysgfD&T))KT*Nf&($V z)nuUv%9Zl=8gBBVhA4C~B{~t=3L8;+f=Q#>KY_4{=xFE~2~7e5L?gr^@cp!C3g>J8 zeZAy>IB_;aGv~-f1*Baz3aAFY8w?TW@F05*)HC|}Ikl_5V^k{SJ@MwwcxWbkUonqi zNL?T!b`Cz!{BZU1rGpOxX=qj)rqLjMlzR;eVf;eja}JX0sN=?7ZM}JVWQU}(aNTvO zUv{1@EQ&SnPf2M*%il8ms+i!U@9;(YOeXs#BBI?Qt@mLE`m-xwu0#%#&_Dh0h{GtCdu>(a;S}%T% zwk^uKhs?Oo_NQ2@6ioPI*`>PDswGcZYd2))B$_gCf;t)xu>ou3$?0^|a72gm2}O~( zr!jry;*E12x~|4d7VF4p6ju(;z0y3hKgT(F&rCk>7u}|)Zd1nHw=#g|K;qZeeCYAC z7McOO+=}tJ$B}k_@f?>-Z;|V{AGD1IA%j1AAM9s!;h@I46msTNt7YF%GGUtz0vJ-A zHK8zgPzwDS-_|5$WnSF$Rl z3CUytJ$sBsOZ!w#*+rVUm|+y#qCL`euzM|NS&LBpPFNv2m}xXbRLrsNv?wO#p56iU zYsp%Ml_!faP;*4~4C`^cc}QCoob<)?1({#CrXZ0nDz#sy9Wh)9%E$I(Cm=0(vbDdNi1p{T>D^< zxEu`E!z^_1MIM~%W(#BMul*H03OPZDId+MrMKJ0jFmBSFXYsjQ+fv7LE>6V(_&6xLd~*JWN%8?Z}tMwPT&noE^Pw3ReT%q-{NQdw~>mQjuCg34u8>}^^?!qlF#5Z~Ea|KA`952r9zk+ptf@&w7r!EhgPh7bhsY-Hn8O5+-Z zW-KYmMed@2`&Bp_aiIeVmCB>O=}$}ZAT82(EPA#Q^+5sT1QCTTD>D(5%PdS2D66;x zY^t2Tcu9zuvBt9BA8avPRte_l#*cT{^n3;Vl91+%aocWZKYXD z!kd#{my5NqZZv8(a=inMt_5jebWuqwb;by3#|&@Zr86v=hZUi2`| zj|7V`)-DL+1+_>(4@W2uF1Lhjy|FB5=W_n!_hWHiioO?citPzBj_*t#c8=`WGC9#) zDd@{~H~6->fu$#Smp$2iX=9Ck=&+;pg;pa3T&(w{AScm-rm!Cj%SJNsv-}ZD3G4-< zRXdRhFncLyHpd7m2WI=COELVUS-yDeumiqUC__6&9D zzrL^$pR7PyqoEfubuJ8dh*ZPng=H33?Ix@j18Zz0)|!={IzAja z>Q6tg)3DW3%_uRm6Y0@H7cn_4j(NogDWQ17a8H@VeS(&_gR(0oZI5$68TN4=<7e#a zvuYcoYZ0-xOJT}WQ1f_w(cVlygoA@zAnJq@FT6`eGnfb?zRqs*CG@!-Mj>oV_6-DR zM)NblDG|y3xInRRDGs@&hHUoHg!`P>PWrdRiPX#i0cb1wHBq%xkQ{%lWxPC)r@@Dv zl@=wGY4c_NBoN0Zk4G`w3y`Musi4rnGEW}|T8fkw**dTcD^Q#lz-70s0$5}WnKZvQ zEoJSaX2#{#3`YJ8(O7*;M%s%cS5WV@?1)O0tn#$==eTL}`8PExh-uXqg#bmL@4L2& zz|We)0GVeLU2dn-{PtC5q@M=~mv=n?(lBzW+f!Kw1R1i3r?Z$4CyVDE^&%t>!{IDe z{U?LL@Fo?ilw7IYoJ@nNDe`!*ceiT^&(d4LpQ?=gu>Qn2}w#0or6t;P>-Od8~T|81(`xZOgD<=hwR=K8{FG-JEh5 zD=bDf_`3c+v3_KtPN=vM?Q6@td~zQnUO79n2yAVCE$pIKUw!sL+~RHk%}O&LmdFX2 zHH90(rw0$Op8}MM%V{ub?q-LYSn|+Ez?HOOF?yX?SGN3hETm->pAbkl&iZa&X5xI` zdsB+On{94L?qjAI`td$OF8JiF_n#yQx~C?paHv4>^BdCuwp=WN%Wh~3Q#ncyg_pzH zAm|_ed@xy=+$@9#M({(w;bMQ3RgKD?zQm8mOSP_hJPPvJGp*sn*eRn)X#$tpdO<*_ z+h9p}-5sLJlXkthwQVz8wK2&bJCR+;FPSOr8pW;9!mvs6BSKtGxE>CBxD48M$rio8 z@Q>6SJfbt}GQSE~mR^^G{l$_^0BTlZbE;f#YeT~8%MkH0zz>5Z*$oT{bb3wZ#MOz? zKuH7hJQrN=vnL-DenXZ9DP58ms(ZKd_4g#L_CK7|lW928c;SAP_Ah@dJes1zqpsc` zWt{BlwAWu-d~a!Gxk`94Xf=pQHBWUk)Z~)I|J2$jm-h5_w;RzZBeYe$nESci=Or=5niTc`6$xJqFIqQ-5FBW1_i)8^|=Y!Zqj=}bK=C^1wV#D z-R5;*-yl2o&U5)#>hc=$E_1YW7QO2_+u3a=>&ww15W{m~BdnFLMt<;TNmoGtNk_+@ zz2CX(`2rcmi90&yL^|!N>S&IUBfuhcPjCg6)O1p-TMC91if-7Grff5mFY|>9q z61UbeI5#19zj2Q}ge@Cm`15#)^3y=G-J9v|L5_S(|APS0G#M*=xFR8h?CWwHEba%h zs2Mtaait7Ih_sRHr~I$jBx}$e zwKMkY0m4V6i|Q%R$b?aQw+*nzyqjqxE^Il8M!Pkiai>bz)kqQr^SQZEe6?@6O_9zc zp!0h|g&)U@c?H8hwjD;=-{q@cm(D(x4BRMjHDIy3> z*w4M|G4&utrLib6Z{}3uJd@=Tq1u*RJYzJRlao-`#e=+d=Hp)x-Y)d5Xw33@D2Bj5 zyzqO+_SD6$afW1={mxO6wnpoHVv#!+7dCfrAyulu<)wyvA?^gG4?(_I5+fKPe@Ol`h_tIWozdcAo`~&(14+Y z2u7$w$g2?cLJcvkDKWpi1-$hGvE9&GCP={KgmfC7)1I@)Oyv z7ju-;_4Wxggjp=?FN@1$aMs4k=Z=P!{(4JTkL;yAtk84PPbnhtAvIm56(B@=TQ3#P zL|1-=JT4m{(lQtdF5os?`Ng>8{G|0lK!B3Mn^)hvrxCL3cJpx^d;yNNR7@S#d_!HW z?itpcYnJ>pQhGb?btdY5#OnK4EaL5BFctpFiI*>EiqWx&p|ER_8e&G3$LTgBar-5{ zOhqqpxhGiggUJ)@3p`h2a@Ac1)TjpqU`U&4E?S5PZLrn(e!AV4#_k&HxY@4O9I&>`9 zCHj_ShOd$Oq~M@zfuyrQn>avn^a5`x9IeM-c3LKRWj{K5d*;=_pC<1hbu3DoU+u*9 z7AVK;PMqr8^|FgDTp+0tzvERxNjBhq>ARW6S<&;^T{=ym8PNti$@tL1`j!bo&yPll zPr!l{uoa2%oM78tbMUQY(5y-=PC9El+7!ISWX#&Jzs(Z>!a;wTQ60h(i20!A{d?Yo zpa813j)-|JJc?t+fjOO3M!JxT1Yoxt;paHmAgJbUIxwZl2;1Z`I2z24m{)h1(IGhY_Kk<(iq7X}s@RYqkCc%h|GfzWCc-2Sdm?_aWa$tF$FDZx~(`#3JG<%OVEmiQZtc0Pu;G z*JO#_0q#GK^3J2+)NbkzU#*s})2>8>k@;lpaDVGuC&vg8z)*cVxp8G$UhrWh&J;B; z?TcnOi&e2dMP_SDS(;b%YFs2r2prXDS)?#w3>z{ezCz76_hDX+#@o5&oTgzp+~k z8gd8Y)}1#0{1l%&^@sYgYo*m`+0aBF1sJm~xG2|8D>`b^e|Y_xUKac@u8P;Y_4%f^qn1@5@tP09?nxX1VU*%o+1vIR{^jt8W6jwf)-vbuSDn#C z_B$gQj;c$e+6kcaUQan+nt`VG&cyFEgEep6UZ{k_@(UeCyK+4g<_b(A>CxUzusuHg zE!)*NcX2Q*JR7@({N0rrs!{wh1`olwLEjV9NipPiTL{^$uYp7u$V}_}Rp~*Qv*4rvA3}f&)mw>7bSUaz>6E$UQWb^)d&_&|eKTLE z`c;l(#>e20!#QKGnTU`^G^0~5tU$w=BL&B1qr2Ese)myqckA%aR~w0H@{8+86#P^=D|AdHEGVJHK2U+qF_nzq|h2 zK!@!G0BewH+kSpe*tq6{^s#J~2;^KKX+1}UQzdz~Q_NVL+|Ac5p5oyqPIz{mqmS*w z#i_zcB%9b5s{ZW+_|?AYMPF;m4XhON$?}UHg_iwc`)w#fZnUG-rs69J^486C+n`#I zB&RNqON!{36HQ$(lUm!75DJwmiw*E1nSnb;OEqi{t!G=5Tb zYj#`|+0iWYcCX+4SS3D0*r0ssZ_NNFor(Zk$WT7u9OZs2Lc|&%IZpq@<_720j-ddg*hJ!S1xIi}_dH|e^2waXjnu5$9MiGlw8d!=M1 zXc!TM)FiMqi7w6=z;2(&%3Cou3Vhb)@q?crl<(ccV;-<$Wm5)jd3DKE2L+pOG)n8{ z`RfApU<<}ww(X68ttfaqcVm>YFx+(%7mtJ%U`<^$a`$2H5E`sxmgn^k$1G1!zaCsb zm?w;r-ow+%B+lq~SFIYHuu$2cKZUOOqCbQoe%1xLcq=p5VgBl@^qG%nP1OhhDkt53 zpo7%R)HHGKTt#gsj?g{Lzkn4t)a;%+U7I~7XJ1Tjthl0EKpW(0nd?otx&!>06kLM= zG;4Y-u571}*j7KjgU`TS>>P^OpH_Dc3A{ehd09Uhd=qfd)HD=f1|L_WJiI=S16LMw z2Qo3;Q4h4{%{*}FLKVk8*gr|`na#r$>SbZ916`l};|Go%YBCIBvbRHKb4Cxk9oudr zI}FAZML9+UyL_02C8NZW^!0hc2P<&Dz3dyy3&S0}9#B|9ykY?62pw^AUl8?vhEWNW zZDOW)H@E?aiCq zHsvcHRqa@euue64`23s=h+M30gv_`iF5eJ0OXh+I~KkL7}Mn6Aw)p||ND03nl ze4Y>`gN()Z6*7&V4-f8z&)_mYk0zCYD@r3?vP$?aPl~_{>LOE$7^J%U8jVT&=Nb{Mzsf3UdVN|FW_c3#v7BGrtLe&o$N1}4LxtW3SeCAsn zE%3cUSO?hmQ5f%kv2~8&b$sF4-`TO*xIu%)Mq?X|Z6}S78r!yQH1GV+ z^`38MKJ9CJ&#c+=tmnS(-!fSNb1u*?EFe(h%i@edkc~ye8&d2TW&D`){1{b+*^yt? z<#M-rQGlZn=k@XBtXGdlbigvA^z`*ty5&7gWk^9m94Vv%)2t7P%eBox$<6$7U3DPr z@ITufj|7=PETHlI#HlY@79tV2Ese;D3DD8LG(XS?-bjWaFntugLEb1LRLJAu+6EDA{=p)7^G2>O@l9Q+Z zxXc;632;IyFlEWXq8h>Jxt*SD7jQRMHf3THF{az_!iP2;NdSuGH9=savL=tRpEU)E z)`#Z#(M5noPq4ECKciJkMT@SegX@)0U4H0RZ#zoD|98*3Ms}0d!f{HP=_`1th|=$U z^*vOE1ebmJ*E!*kl&$ohb3p?otA14Yw^qcXM;mWCu7D{m!rpo7rh**3tU*L9envxSEr=-3*F#9 zC(BLa?cQj2k-17#1Q*m;If(|gF>jJ4G^5lUl~a=r>Qo$+cj8zqG}4D!?3{R5V_y@% z`IJ(l6I)(GgBs7z&mFV^0x=W<-oF1CZYC;~@+O&#+^FCn!fD!vWy1%~^t}aJm~tVW zK?9Mm##kL5XnwGis;ggFp_Zm3KSRCt0QYti>|Lsf@GSFbk>~9zJ&b}jX1?TZL3{x7 z0=c)rg^+#Ud1AMozf9KfS`?OUn%^u?@&6`|0h424cv6kGzSqwnHUZgCC!X3X*K7q4 z+}8wl?=-dw$l6wv5gFN>Y zhA~DW@vy%BqtKKEmreRP_v`3_Zq*3-2({Ba`zNH}T*O1Q0HyGtt`Bk&zVFP9uCE@q zGPLTn(S1HxLu~%axL;^HRO9FTyEXD16 zb)>Vomv7K;BB8&|Mk+TLV3YF>)WYL9#=MCKqFqMVc6z+g2!`gCxRMh?x4s#r96Hb8 zYb|y?LC=RBD_VmC0^`xTx5?3dX&JNcIF{?jH|z9IX%Hop{oydegORGecmldY;;=r+ zIV}E;tJVt&1ZKq%zK4mrWhf5;g1+E+S5W;C>@=3pDb)Rp3?|6#B%$%5{?z2{{x<|! zg##*+5v5aT zP6SAZs|vj^0*Bu+Q~gkEk7?J!jXV_H%sZ6rwLWZREhb+;`_xg=g`V$+A(~b^Ksq{-DJv%-0e#EVZ$Y z_oVX|9_T(9gMF`8isrBNWVA4g%2`_3O!9>XURK3IDAv)gZB{!p1OBfYb`nZ1I!y#F(O| zVq9!4Q%+8O5nag(XqFD0aCQ5k=#pjMGe_3z^SSybd&%QnDFZTrxM>(!U)}G0^uq3g z#2YBKW}H1prT)gq1zAxcQ?9K!QXrv2znj;6(~44#VCH3|zGn?8AESXIu|%EBgxgw! zf7Ui>5$3YVA`s^>CnK-3H1PTsg4I|ZX3q#3FpD&sV#Atq(Tr-tXBmz0oM!U!yB5|= zr@oCu?kHw$c$JLDkj}CE?|nJDohXyVm}d`CcfH#AmFbmGpm{^g*bF#ff!}gD1w{}N z&g4>5F*)b9;7EZNykhgt6F-_qJub&|s@%4{!PG(*#bTD$0Oe;Gs+wGHmjjQ|E^Tq) zubEzKI4wT^B%ed6F-+@z&f&4LSBEPugE96pXM3eg53kRhkt1O z0Q02ndfswX153^yVR>oEB7)C&Wl00r**4a6p&bbgwY9hp)|SS`%+|T}!0ko_;87%P zD&Wyz`UDOR>^^L10)mL#zsSG;%bSa_aKyRcc2}ciN(7FA^gvg+3>0;xegEGTP*l%9 zO1fT90OA+CW02-n_hC#4=JVGg)TXMGLb^bh)`^<+u2)-mfnBN(!+!Xtm=dJ-wl?Vp=D|PQv zrgTmr3;O~yIID#Tp0W=YNVv<$1##v^g_97F_Obf0WeEjChy)uw95=P8hXX~qtV zO5G7ICVHVac>*!Hbn=uH+LQ639Cx&p%m0P(rBBf67CuQhte3XG!o@M#LDIleGFb7H zGm{l`_dzJ-w^lJgEaawvxYD9bN$DqHUtg+R8G~hOkU1oEvuRQcrB4`nVlU7nRT~ur zDu$~tdHVQ-7m>1T{U50oGCkNMN0PpC%UO$L*UP9@G`os%X+1}|(q2SL zUKCc@&bI^D($80*kvr)OCSQ`2A6~^FMNMf+7VTqp6HCH)sRT@de#}mB&yP{?5Kux* zdjSZw1g-m1vNb_<4*r~Xe5ay-2=4)@#O|+%tEuT9qvcFQb72yq3uewTkhS zW|3(}d16nN8xhJvT-*cLOZ?$`R70ZK^>$Zq{i!VdkVX4-o5(q8f>?(2QjR3UHLR;7 zrJ38cp*{!MuyY&J&ddZk(iBWE&7Apbg#G+1$2yVjy!d7#P={nJ|Xo!u1ra{n)fVDvp1UbeKy219ySJ)w|3dGvp$K2VrEbhC; ze|A~+IA~#3bz@5o?CCYsE5PJ}7}9j%_$+LNpPeM5=BM`@MS)g9P*MbxeQVkn`5d&8 z$H&^4zlD?&`iQk;)NS71eTDw8fU*n?%W#9tJkh?0*p;r#zxz{x2Q|gkH=`=j6y-!w zYmOyGukLcZMrmTexg%#*hAO6cb|LmZt#39ghRX z8A6k=Hn8QI{MNfFHJ{aqki7gaPUMxfI*UM5ENy5)qUe&+W=v*jm|%U^W2sQ*f6eCc zsQVNu51M-u$o!F+Co{#}=mr`&?QE?B?=#y_;JUK-EmztloU-Q&vwZ(d1K@+(y zrDT6Dlo@eT4_y2~YmW*@Z{DRC;M@~Is0@ZVBof!`2%7wsp&Y&tQOPuuxs9YDmE}9;<3CVA z;rX0fT*q%aos0Q(hKD150fy`tPca!Rw)+BDFoS!@eNK`^T9BB^eWM%ksN z5S4xiv;-{(nB){LBWY#}f^b>5Z)olGPy4E(_{Yu0Z9%vU_uCp>G*fzCnq3*6-7lMuGOUc6H-K(jDE{Zv&?9V)f($I z2uoc2KnLrYrw8)~d~SB@Q^H2vEdTcnB5;cKG)7aHx_77S7!QU^BI{icX@Mb$lOr*4 z2A@J<=mN0qY<1U>j@_1Jn!I>ot*UR%SB%Wpz@&1kFN4tN^C{bETqE|8aNc5h+rAQH z*5V0UrjkVuZb1&Zr65u{AO5=MjVWhIV4FrTiuKLLH;W)hwphOAf~R~cVt4$W8b(PH z3z{lzt2I`2r=_(%KV>m1^OMF!fNL*ogMl_kkqS}l$mQ`}h1oAm`_~u6UK(etV!%cX zGc-)d-{a`s`SJwOPY@S}F1-8+U!02r%ok1Co9i4_`qjUOh%%lYtCbXKKNc&YHP2P9 z8A4|YAWY?AwSEo=9G#WMrH=)5YueRL;kIVu9?>Tx@!ix|5-PHJC)DIApD-P13ArN_ z4h;Gbv@k_puPD&Au0;CUzH3*PJr&9ypU7V^6x62TXXrzXE$wpDrm_rc@L|lCzrL#! zJ0tfTv#Ci}ERe!8>oLhhr0?}w6%&P>kR~M6r7HEN8V_oz>+;%IF=fW8B;fNX5w2Mx z6N@2G&2tO!1=by(qntQgG}j6{p6A^MIxdPE2Z^Gk?a9JR+KRJ@{g_RZj+*^}r3h+$ z+@Io3haQ3mM1up1nQ{#r=ku1)B=F=peuGFN3)V<2ag7mT^mLslIpp}K z^PkvehI-8X285?R8VhE4La9pNf|)1I3)9GO_0X#Mxon0+K@UN0^CBR6vXTL(TBGyvg1A~?0K&#xi$n-ya+8(9~r z4tM;^DO>;UcrQUTrcuyUx?Dunfpp~`dK}Q|=`$E>TkZYPby{noVpq`LCPW>i;FTam zeP2Y;UvW9d?eadqI`GTt1&t{X?{Vhqs;hm)ThQ2c2Luv+X_w3F!u-uCAjn0h{xAJ1J}TGu zTSdm+hOT=u3mT^shVdrOnp|2cm zLp5YD#DXWr^42Mk*UHv>+kM@w@08|jmPudNT`kZ!3)B#%&DAMU<9mczwmeQ*jq|m<0u3?2(2AC?V*WKgK{Drd7N>CR~<- zDjvh%OW#sm|I-##z27Hgc$%bp;%8wbD(r*fMkdM#X=!OoD!5a)3RatMXf}z++R;{7lAlNgWbGEDu@0*Z=YLpJ!}2bsFj_eZStk|-iUFQWWPyzLY~6j zQ`kbm!v$)^{ZGi=dfk1yuSN&4RZoB4cgf5|;cC1~ak4K%H)gpGNq|XnKu0=R+dGit zxV{FC)yn<(YcPN@+QhrK>(Sl*=?J3w_q1OMbY3ZqLys@Y0^q<*CXv^VuA=Bi3n6_$ z$~X7|Td)hBClG~#UAZXrr`^GG@^yr<9%9|s0e&TH&8DjSn^U$noUlF@OEh?44lDYg z188*27w3{^H;Y9M3P7MFxJd9Q5__?=tPQ=FDPV#Xk>H8T=m}hVbt1IiW{Yw9`pcfs z#Ih4wvEK=Ch+pM6Q4OB3kAz?R)fi6*i@C_0Ll7b+MnX;Ni}%A;;5Fa^dPNy$jldX% z+pmCKD_Yxy^aadY8GM5h>@S$$3|_46zasN8V=Lv^T3(DRWQp?v9&?}1Vuf@FVHHIn zm!b3B{n5a+8)i59o6UCrxjVjieEy2i?YDB^&||j{&&Lq%{=T|sR@)>)d-BH6g*7ZK z^E+D-3O9J`ed2t1G(U{Wx*OW}Xrz(WG5xIa0?wLyN)eYJ2K#FdoNR2K zJ8xb8g~We<-67r}{unNonK#YDkb&FhWBJ73{sqpJj3#}+^)S+t^fx6FD;J7%BrJ%8 zc`&NOoB+oil+(>27WSiiXp$kaLl;AjNC`8c?cdodKW1NRqfjDa1-P=VeIz7M2J0c;8sx0=u%b_f}*ar%`@aFbgTm5YR(oly zlnDYYK5JVDF>mR3s{GHB(MKC7{D7ToaF|H*apQFcHh5zlaQvNT)3o1gQZEeTN*;W1 zI_aJO0;16wJaxu{wsE>>pP6*2+V0s&h0CDBT(}qqTJzE&^nKhpIEdRY9#Xe~+|E>C z`ZV^?m4}LcI{(va;&yVP3mWwlGb{X1b?~DOw~GP}V3^ooEf|_46CW%SMwP{pj%MBP z(E0YpkI%eQx1fNKphlz2f_%J!Elk!J+U5D{%LwmCF9guL4ppy{Rg;{oa2DG0M%WqUC zw|>j!mg5{xM}1ZRtD9NYV7g?ZY=#8tzfL^p3Lqo(OqLUz%dZb_nk6W(1a6&=OQ;t1 zIJlw)t`<@nJf@ze%vI;_+^xx9Y_+WMl#LW%DF@A1$%jIu9N;QPtX{u!H}<9zpzNO0 zOU&a8caxOSi9A&}-|MkgtoPV=tz!7O{ z6tNPf6HR>e|I-pgRmCLE03GKj!MLfEIl*a;DzTBt0${w<-~Z8x$U1?{%M{YK?>I3i zTqCn-#H5$w7jHiKWTP2uKIsJ*H|SJNky-*FPZWKl?t$YJRFP4*f>77 zMbhYl0Ehwyk+_v-WZmtWl@7|!vNCXKw@R)F*BsQh285Qy$fifcD}RwFVCO37d@hu4 zUk%ephGb-1uUuluub)|zaPZ3m#9W^$o|326TZxDML!4VCrz>4`t{B-PRJDPekD>jx zLz;he(z5!UbrsJ)s~f0ki-LK+PzD+-;+YmXb=0xMX5p^*2@0Yy*GXU}M7l>jW=OG& zFr|ZV=UUkFh=r&e5k~R7+HeHcf)}&0pu7BK#)_+O_$$Oq<_%gEu=kaL*{V6-4>gU} zv4U@+9@9VnZpK0e;M#R66r4?uf<9aLi?%PKKsLnn8^gJxfFb6bCO+eOd zc_8WJGdG#rH-wXA^=8+laJ&?UF+Gu zwUUu8hR)U2XsN8zt$jG=!|nCa2j0wJ$NJA9 zcNeIk7FMlO37l}5@4AnhN+cd?&-b#iQy}R`VDlf9|4NyFPQkM0z4?5>U z`O298a=x&%RfLPX3{XWSq>E_h`BX{k^-p{f2F~{h7w1h8H+j)B84c-p2BuJTqefe{ zIDkSN(Ihs0(e7gtC?xt?t$2U#V^@7j$kPuwn*5S{E)Z}zNq~%R24i1?1YB>}Zhuxm z0e_wQP6vqlM0lOXcLx%e*3F#{hx!(CkW(ijIOxRr@&;=tRV%xKM>?Hvi=O~X>Or8Y zUhCnQzB%ggKTdqBxt!nMb1it=LyX?HBT|-{Fu_rg-Cg>1GOSb+vYYJx|B8i*;j2=MdWOGR_gBpc1)eOT;WW=G@yw7#> z`1|tadONL~pp>Bgtf03a!boCpo{K1{z$LOkIrw+`F3Qt+OjcM;3;drskD3+!`AaWHrmII4~Vqg6|%?&g9bP+@;Qi za-lt18%==D6%?p}vtvxS8NfkQ&#LC> zd)9xZNYrCO>T%uB=}IsSrHF>0qev)uptVxj{MI@d<9qBRA_7M`wx;i6FaLvf856A^N^ILi z=pIx+k20AoFm9k0Q_Th5x&?i;{gfOt&6#|(tr6^5e_K$ZR*MovBpN-cJwWojLW=S)A0E_B>pUmomgt z3BSB3ugjZcY%jUJ-g#QdtH2TGMHj-rWV#fGxfaT}$SQyD%AM+G)uUx2?*nvTI%?GE z?MiTd@<*G2$s|S+DHdpB*`3$rB9ut~+2XJ?yOpql=BubJtc0GnQ|!aUgeSCb#g$8( zWVF-d2O!eQ?Dv--g;0TQR{sFKOUR)({0J&i!8i}y?&tV+%8@p%u9sMViBN?%*D`K> zUkXureend`SF;x0lmJ2mJe%J?4g8l<`D-?9S0WPk3QTs5UbT)1l_~`(2U|Isq{6aq z#>Nau1jo-KSALc!%&Z0CF3JGGhu6X#1l0eKx2 zbb!=orSjKyFCRNTssyU)kwD!n-3441H86055>qX(ArpKB7h-TVKLSv*X!d_x_uiCg zet)x}_&is7?}HocsVas4rO?u@f=5L%l)V}mrnX%Hro~O9dz=6a_&9{}Gloz4{le@0 zVllp~^HO(;0JZ>PPr-CK&89}nQ}36pr&OSL+U@)NHSdK1`sy}>@^00G1NLk6`i9rG z*|bIB{;%skn{trB{Ndqf{O%en0FdEXyJdjo+zNVZwEfAw#8&7A2909KW*&kk$xdXx zCghnlIK4Ru+}tMEeFowa_fWoH0asQy0LJN9>|u#QQNZ#IFkW?{)4b3BusMTGIknbi zkQ1j0_|I~I0&9O#vlkt!GzXPT8u_q2ww=1FbCsq7DHY`{~EV`{gzb91+^$`EL9$GYGpIma|?92jDaw#ZU8gV z^xOM*o@KJ0#BU?rkdNE(S8h>nB`!u@rj7Ml)$rrSq+B#811l`UqeLdpc1)WU?IDp) z)tUqanD_3qb3_j2L6^6#%`_g-OHU}p{@ysgOIOD;qK?~em@L>LMkdN5rEDRTdMop2 z1FgcY`5|YVcwk`TvW>uZRbRx)h87I))DOkH@$O!zK>vd9@o`@OfdCAGiia5DP>S9; z1SsF#ET#qU`zEvz>Ht9(_FyH0gLm8c+`atVO$At6JshO#m)Pkv00_Zw#cNZ8VBZS& zpMmQ!&7e$pm@W5gI!5HDIrZxc7+x}y3@ztKU<{($4~UoPa20p4X1jJZ`Sz=3Zw|B) z<_H!{St^Q9U)|m1mV73BSifYDd^*`wPxDqaJ>fNmW*G(;g4Q}!n*+WNnVNS;mnFyQ zxmT}L?x>DAk0h`FXu%Z{Ob|&ux=8J`&JLzdX5izJO|QQEZS^o^E3l0Vlei(6`Q1tw zsh(|Ng0WOkNR#45=F(f5rURfJk500Uib9yRiU5%?7V&J4jM;;>>5go&vUi75I&5Xx z@N}yqS?ZhaJF$^O*gQDd)I-2P`LZ4{S_+4yPCXE~hkassp9$vsxAV1)gOkhQJ(D!8&o-qU1@ zwz(D`*=%zO=gAqi@s-eoole7bG-{>mE&0k1paWA$e54jDjz)*EXgbHoaJE@AE;T*& zI{(OzG8^@Jz|(0>(^VB zp8)U80e4@}P1b9LgA*;ORJx0B%s-U#mvj$zT1cVw{gt{_y>;mWpMrH;=X*WnWrCw! zcDhmanmdYt-|u2a8stSSas7N{0bQg1)_l zGpox0p`?^yXnBn%w3>rM<2pV>rT8(1J-b|9gaIH4d+PnL(4WtnSM3-?6Faub$znTB z#Q+)YhMkn?mf|<3QMiA%C@ITHy7el@N}6rz!!ryo@lZZGrumI#GqISJ#t&7P zI1(7j)cxGfp#qS92F(!A+09QM;len6V!dn@rD`m3NI-{>p2JFl1m&wG=R?X0hobK&3HbWQ0&vYI~@t;~n# z#%I~1RdliateQ!$XU5U$p^`hDs=(1ha1z=`WwzxXQF5rIj_Kks*LO@<$Y6MKgPKFX z9WeS=0d@28muD0K8ru3reBckZu0RTUkE0U&zt@zg; zFk|$dfD!Xy>XtKuBlteqh^b!tc;+H^^Ud=a`}eMqpwNk@MsN@3N5`PbWKVHS%5FU6 z@JUR)dVz|nQibkW*b8BjVSxrm6B4#$H#spM5IcESVgxQA5?JyJ5QHoHLoXD1%{niP zqyWHr4>eT^fNHIws5u>W0(miD3SSNNS6gFzc{TrYiX1s0`4AB(ZB@7$mYPjv6}#pBeuyoT8&q^&Ts(ZGTXXlG&bIU)H%@Gx@7d+oL)EeYR7weIFG>^p*!(3j;?u&w zH0t!dwZX-vkaSN*b z!)jQtD%Rvw`0ppJx?r~*vL6)Pmjt!FjbwhT3={^bNnV5z8f72>Y9XcFF4Fck+*Va> zP)Atfkjgb`N;_K+G;>*O-wX#d=?hGlB;`%FO7qazR?GmP(}ny|6Da8jg?N~3h)^i|5aG=N=kF(Jbz!~{Qic0V-eshUq`UvRyDS1j=H za34e)zfj_Pj!EPlrBWw@0!omVlgQ{U{nl44w?tI!EDu6PuR2KGRmoTVB*U7WXV~kA zm_{=G!LW3(PnCAq$(KJJtq(13X&67%I;7$#vpWXL!PU4Gsweh*E9~x$ldrcL9X^5e z&Ngv34`gn^W`oZq7riMXVpB<)%}D^ue`RV|8NFZ5huG6m=5S^~a(MgEXE=hm(?HBX zIQvO(4o01@@0<6{MiNVMfYToY4~dLXhj-4H_1XV=T^8{ej6Z5fK~ReA_q##Ht)9)h zdlGrGfP2?d-LppM?BM5if#OV^>iL<>!7a3b=gpP3NrO z3)0e}qK2m2JcSu8?c$np%7NsA<*spQKm}LRF-Mznx3_xQ`QnJ`FjBog=EzrOLr)zo z=Z*Fh>2#{T6$2DGA_3=PN*xr_`SbhR-UPqQ7G}ObpS-#_y#BD7$DE`F2{5egI&d$Q z6nEK_Q*1|unKIcYKuZ0E@}H{l(ojpwWynC-TA>h1L`0URn0$gdSs%T_I}_X_MX}Z_ zY$ra7G7b8DNNvUtKK(YbSg_2HfUW4+soYWsJH)c&zB3Z9{awcm1H?hpp)#~iKEyIi zOEXktJTpm`?+LM9DR=T;7&g1S7hW9_j`V4O4AVGTzvDe%l3(DX?l&8+Dj*1VYKJtK z14JPRplAzM(%`3mg(y@KXm-->kMnvtOPx3PtJ>Hn#AKZN0LCMyPMo9r(co{KniivW zH!|^bV~hzqHc!{%4&-XzQJp02a7)CrkenX#Kh@Sk%{n9Lq%VXs(}#ZlM)x&=4pKIy z@O^#sMFI3UR;w2Pg*RLN{%D+Ww(5R}xiw%MBYz|yed(D;L78Mw4>P#u2*mR7NeTQl zq^Y2ZQcMi?z6t|cM&;KOJShi)E>73Ia-gf)e&NQ)?1F|JdD3EcuvImAl9eheik`N1 z5t~gspaFRs5m*@LxBxkbC@D*amTd4D#{xpA8wkNqvrp$ZI++3oB@MlQ~m}K3ABZc$nq7kA5w`kTeGS+2HL<-0EvsKi5Lm* zw4e0Pt6yo0I_m`Z^tpg-5bbht`>E$`x-?4H_W&9~PoSD+%tK2UsS{WS<(H84-EAMV zG10}(6*pOU<7rTuihw zc8Z2;yUUNt*2S9J9dFmewYx0fu#ca!#_7kMMSAd&PQPN%MNa~&qyMwaB8fL~5EcF| zG9OkE>i00}1&vmiZ8>{o5$bNV<2AOl??AX!`GwQ_Iv)^78U(>J!I~GaS5HwcV1mDL zA=BUFGH7M9sVjdTRsxGdEi&M*lH$Sa+`J!R`Fw;~2H?L<^weHGLtAtI6jMyov<%~N z9VxHF!l99NMm5BtC_SdTu}hoflKUhr-i`BFb&WPhGl>*h#D@XPZCG_bkp_QDP0 zJXXQ=<9vk@niI(IdRTA{SZ*+sKCMyZ){o#h*l3vpTzslZ%2?k$%Sj73L7+ihBAu^m z1qMohX61_kf>Ts0MLJXE3UCFkGRw56T+Q*QI>C9#6^#9r5yLM9wSJnUGq-Gum#pppcJ0D z?jiC^A+X>R0>*$(LSDlKqfQLu>b%&{3`TK$%1QLhUFvUUrVD-Y=yT4?kqIF4tN#iJ zgB!88XMhr*xQ-p>z-@muBgf0tkQ*mNRrK#|{r1|{czlDGJCIXlWSj3mZ=FS#3L>XH z@=Fg`X31as;G7Ithbxu4hLy%^n@7}56(n7shmzEDoZ%m8-Uldk8|Nyn7G+T2gf;ZI z46~b^C=jO;lyc(tB1U}(gAcXaWQL$7z=`;K77tkep!7rq)BENpUg69F%iPdV#L`dc z=hRLrC8j{Xcw>V1H>dYpcInyg^2+M4uTANf7t8{SIzYL>q73eykKpD0$zJ>|5G3&c zW5O9s1vV~jK_kFzap06?_&_a$UYyfEQKdncJCcyG`J%we6e)Q>|#K-EMU3Od~ zN~){Cr8O6dxX5_GXwS}NISSj;djO*kzJu}5ZTA^qG%~H?sS^Z)pn(d7*~X86j8eSQ z9mo*A^(g*mtm5z)fYFeof&gfngZgyz)EL{9;cG2MyKqH}_Ot>rWWS5;idj8ljB))# z{yiQ4^PiDcaR>i$8Q|}4X%L_Vx+p*}Ubp&`k#Y65O>!jvLHFC3#!>KOu`G3btSFO9 z7SB%09Smmg?x#(3ddxvV+T6El>@cAJWLWCp57d%tcy7eA9gM}xrf1izx<_kdZ!CP> z5q+B*o@cIsAaTO2OTJ`QH$dVf4)QR&Gx^&3sH%@G?RSj09vbzd{S^I+m<%gMREFuh zW`t-x#>mGk)n_KUo`b|eI8V*so)=D~T1xEw!fH`4RMkYM=r3%((u;KlAd$w!O@HQgp$dRjEV;>ku&i4>w?5++K_@|S(B0?d$+o%h z_kju2e>vRL@FK6)2fYBGGzXtOTx^QOjZW?FdWB)NE6>IM?SJdds|wVWs`>9@|Ju zrqqZxb|n=3-aLYJ-MO6~>%ZOzu09~$RE10q_pqb@i4!(pjhRLZ>&s}X8&A{ctjP!( zUXje`;nCRI**jMi%t#wNsohpP85aQ!EKNnMXrL^o>Lfcc6kpUX53I+MUT%J^pY8))Kc%Cg(-$T35n(~OnbX(k~&!uVqhhk^PEe(+)B0jUH z(d6`_;1W%E>-zRQ>72*IX5il+MH{#^=D`d(pvjulkVyii(Y0qQaJ0eb_(%bD;B%u6{={SWz!%Fc!hXN9o!8KxqSEGJU7~OSJ1Z!6;J}i@jOB- z{_ANiS9p@gIPnPj^#e_Rs~y=gcB)IxIe4qB^V^DO!ka2u?c|er)V!^tq_E@M#k#4= zZ>9yN_&=1<3G6zVy+tDVBrn%yd1tv3E%fa9+=)?Vfz=2Cnqt2~v7(9X>eg!<=Xqek zP32afvQw}v8%|39^B?Ptd}2@wr^bYCB{Vv1k(XIywjuazN%ylbi>;@pCfAreYD$^v z67*CaeSGS_?0Q@QBB(4=gS@NTm%fEyz{5q1@FcA_0GAcF~i~ z+zn6&zD zHIYZtu(Gvm>mvQ>7rXk$ zHQg+E9O@IWH4nmPG94D)ZELvGv({OMJ}1U?|2yZm*4kF}^b{g} z9=ul3G72UK?EofMD!}9_1m`PehVvz~557WsXJA29Z#mgQ6MLzBCgNVEeamEnEt8uO z#yg52 z6>Hp#LLDTeSsI!#3YHER4b4-5h4BEx4RCox>&e%$hr#gMek^y74q%S|WUI$H(6^x@d9Gk6exTNiJ4g>O9CTEf&mtcR>chv{EvbhR~?M zfdVnL5*k^hR}-z^UWSy~sGT2lNziAAX!^@m-|jS3 zN*M^0xV^zEnRAz`!>lf7^|^HE2nZ`WVk^DB%OuaG<1N$%+Tug?*GYhQ0p=cqR*e!ue;k2m|onVxTJygq~xL}36 ztKiK8p)}ewSUIn%rKxu!)!-Z1taDW+A4y$$!SonB-b`pL7BN{B+7(i{ud{lS>NzxSwcM=JviPWlCV6R>57DS#h3W?*@3Tn%aN=G0V-{hab&q`M zReZcQNQt;n=%gZjo*du@G0GE%u@w0teUnckRlUM1cw8-#^0PiMAPzekO*ru>2T|SB zXSnJLZn!|iU%JqS%V`n_`$F=(QPE>0PQ((i2@RRZA!}1bkuJr3A{MsQ+z;B6V0K6% zsJ)(+O6Y?pTsfEU&MLdF>5ydyesfe1=n3rWn&`QF{AKBnX5AYp)X~C_A4A7Cg}4O^ zYj_CUq?{7L{vc%ttTn7pAywrE%%ICs=^jcNAbykeSSFyJ+v~Z3yIHgx4>I8OWd^-KTELQgQmX&% znsVs|u?N`&gq=obW1R+p2l@wLV08cSBZxro z^(2N6fkdugv*e!-@ac(5RvHB9N86w=Kdg}Yhp)ul?^8eXRD{J{xNfFvB3TU?f7@8+ z4Q2uF^QBJ+VE`k;9t=`T(fYDq;ivL%tL+W{Vb9VS6Wyb(z7~F57WCypK6)};n{fRX zk?Ys*#08IywY8-oe3~muYo>S?7tITT;aPLP?SH^M6!Pf2pl?*Z9tp=D`pE!)4Lxl+%j*R!5E3%!I{s6!ZgLaX6gouW-hKww<^ zRVMvlf&>)Q%ropWxwaD+9PZl<>FN7rtNL{ta%NL>`b=qI+oM^#I0)pP`rk{uYUgV> z#^ZcIPp!*AB~eshe~|OZV4jT!Hh}5-R?H?P&I8X9d3XKnJK~5AgvN$jdIR?1Lb-}7 zEMw=Eh5n&zeaKRWMMcR2hfU6j1V#{r-0oY_e+_4auUbvU-Apr+_vpV&EhFYJ=|;f- zk@^M2aLP5bK#o#0E#bBb;hV>ooE>D9>+us*5EjHRc#FB5iGxGS;Cm8>Tw*aqyX{X> ze>m`ep=AI>4HKUecRf#?_%ri=@DgSaAVS!))e{NDT)4rffMgYrYJ;~oOt%0sV^CC- zG%$RMN~a)Z8iF^>aL7Zcz*411gKN9sNg39>nf1J^D_$n&3{23LJ=&SZrI;xm`>D~` zX>;2!7o-7;uXa&F{~#s8e?xG@6Q{e}3@4A@9|UA{gtchFGpys|Tq8k~BNU+k8i;w5sL8MRUK(g83sbNTi~irJR0A0pLZxxS8ZOIAHwTO;)WgF4ax;sV846;uQaS+{~F zGiG3=O4{>AXNfI8JYYU6Q*$@P^!fMfqd|6>p{;|p7C0#hE}e4RZ0++{Qi@Iz@SBe2 zGW)vGQ9}Kf3z>-&p~U5W=IKf-oaGIh(qB9@6ybA^5AOVTf~(z$uaoq}kJCpBQ%#k^ zhA?zkuQusH63SWjA}I+_HJMi7FSFSIdtV&6diNs7c?2Nt3L7B=R6U4lid+hz^t0LI z7b!QdO3?52_ZA%+0>fpRiBORF0)_uO@a1htp>VmT8wpC0KluT^_ba#bfu8f(=2h1S zQm*Bu+PEO#z=-BOTsR2$nkdWUlaUPq7xD-$A&=ll7k17cTPC0ohbFr&Gb>ALx)|WO zb6>Fq@zRbjYMDjRL%-Y3{6CVeIXtfJ>rQN^F&j5-Y&KStrm=0?X5&n3HH~fCw(X{| zlkdL2?|J6$nLGEKv(MgZ@3q#cgwDv{{@x=i-8vJ2?4AulJw$0#YirADAd2PMTKCF; z5DsyM+<9H>977pGdvCC_yy{cy*2Se9^-u7Aa}F9Rf*BL7K|=qf@P?2sAKdmPx?}1kTJD+UTG-KetsAl?qOAxng4eU69k=22EKgO`I#@H?Vnh9pm zl+WjL8QzB1cbD-(1jO0(;0vW$Y3GHw{tG4(30rx8KhGDZ0lzm974rKmiGOWhGfBforg5*;Q$# z8qdwdiH*{u!6JV-(PVD#`}(JDM|RaFN1VNR3|(dIwcy}A|4b{!>F<@Ua-oz}uERCd zkpdc=DEHfyaUEuk8tGK|!diuM&MiqeDnC@0TP;yai5;yIM@(ws0|YcQ6FYx<-LsCa z@75y&{IoP3XxZNC?wtPEz$%zs66JT9$(rxXR&dNHq2s|AZXrRdvkX(4TkQI5{y zk!vM<&d{bR!7#$|y) z^hWRqJFv;r-^1F7{m}f`ouZ6X04a-x9QSSKRV-={V8Pi?6{Q}=KHs4CSOvTkjb*{k z+adw~G0^#3wnXm2sz4}=IxHEoh&6ve-OoaXD%^iRzlVPgq0Jvem#h-WWa%D7;|FBA z31e^wYGk(qD#|)S4i*DJy7r%1Fuj(GQs0- zW7U4lF&%KOU_DW|iP>x)IkbS(&V=>7nuKkQ<(O|7!7Q4cwJd7N`KXV#&)KEF@ zV2DSMy?z+!0o7+b6jTCu@>Jfbh4ZX%KB8{GJY-@m0!_*JaI2c%z` zy%mZ`>Tv==(dp;nhP50vaQLvxt)pT7uu&zZqD=lwAO^3IxS z%7^n7`jdHukstPscvtwt0F`KiRM=?LJgkt4`o$=8>WfB7}$r z3JrOf060^DZBCtu;-ov-j&!`hM{n%^gozt);dI;N(~G%@x?8aLC$siu20}PNi z4D~th#Yim)tOD2$^qBrVjYG2a&NWx8>9DSo+n3D$HRIVjiWWv43~0~eT(mj1%4Z`Z zY4eQjje3v-_`g4(0iD-z`<@e9!Q$c!HA_2$^=OYz-4DNphk=#MyD@&E^!GbS6Oql6 z#{rBHiR7;{nTP47Q`f+sNb|$-<2uzo8x}! zZF1E&yWp|*gaq#&vVDh~h1Bo+YV+fUF&Y8ndYs&>NYUsQTNq zG?1_x$!rP0N`+oB;Qf3~N(rZpHOya>E&7tuNUTm-lwjJV{~0>fyNXat%}ZVLcLcj&KcoPI(rR}s+p zJ|D{TUm&1C>`!sf@;=72ASFXr@^ro=$SZ02j(&0vQg=HZ5@Iq3Bz;K2X7_OL3Ol0iv(`E_efTmfR)&XqjBh)y0H5%-e{ z0VuPnlr_cWyK42Jc~UZRr#OT3@pAuxUE`(8WmI9vB7r!_)<^#9NS+#1P9=>&5{j0i z4V^J<)G;#h`cS|h`n8dO6Z1#`y1RD5t&tMC*&z+?$h`B?ob@#>nw?QmC6&PsQNL*= z#Qdb1$lkb$)S7KW2NSfmwEQkYu@ghy>|Fem{tfOG8&{EQ>!FU8Dz5TfgP=?i%nW)g zS_zpZl%d*-$bDfGiDN(m+J{vDkTr|nsqtq=fp~|oHH{5H7rh7RbGt9eCJ3!6r|K!| z6Svz}P)>=5C4OVYRaz%mVNxKj_#;%{{5u2*gf56V{AY}R2^a|ko2C9}NP`zAPRo%YR8)odf z)gS{TJj8+e;d0;J^ZOYvD&7Hb+9V#Gkj4i5X$=9++2h(DDDz}YG%z~y={iB6RL@sr zfWZJG3V)y35(DVTGNWn_51eH3rSi4&J)Qk|i?Q3{GaMxaUwBOJ9Z4d6Ho5i24F%-G zZ+5a@){7MxG;7Tg->+inini4k=lJN~-dH=QCYIdJHJilq<-;_{XAepI?D0^=3;ty&*_0*b-u2 z-FKG56t$;>+{l~I2aEg;i;^ULqg%qeP5q=5Vxy0c|gAmB{+D<(}E zozB;BclF{3vK?JAeGu6bb++A@N0zTl0Sv%r{@RbAxP^!Te+*Rkh8SM<99jwyh_0Oc zOLVwrR0oi_@&#a)YW{_0A#r@5jvp}iljd8a4()@mejkVHinY|_0)&S@&R=KHbURh+ z+4(5&BUqW%nJQSm^=T=e+r#T3QLDH9d&UL=^pKcplT+Ytywbjx*Sb6 zG^vIq{9}p6hSn|<+(v>28oqtZIoX1@M@6c-NnWqNZ=W6Z6k0g(#RH`1c$;M2?y7BV z{3g%?zu19FoUT(Yc{13RMl?M0ya;jH#q0)1-qGri&=F(;lAK}tXt>FLR{24ftw1YT z9I+eb*S{2E1D%*M3my-l0YEVondHLytEGmhf1uW9)Qfuy(*=!_o0n`o_P?xELjNoP z*?(aEa%Zb4ibGn99#2$Bdq>~}&_j3A_S?q<G{D>sFzn1LW&t7`*BP*g6@CcYhpVa56L{uN(#mcx@5RCBfs$D%@$p>A6=5h z&Mn>O`02J>svESFgsmJPiV*l$LAuYK2hI;#CYsQL^neY!gZFhXA_-Z7m#;oEKi2^s z0$7Y7^ld;GDrM5rNkTH*n2H{;d)b1ie^5(pK}A)h#w!smSU52vft=R!p$%#EFOO5- zQiVJLaA->bCUqDH9s25=nIt>)Yo(hP|IPEC1z=3UtW+lPKOYX!O!=&%#ovXiDtdR7 z60qJ7d6w!;u5;O4e&c^HXe#q&t?GHDc{=^KWh0#)UI))Hn!l?*7kyaszk&zm>px)s zkyCEfg1apfi>L%{lrZr4Hh!h%e!NoGdZNMrB-{U7L-y^T%3Tww`yjnIUxewVE1ICP zJZr&9Q-DzBJ$LLGZV_Vx&gJRu@@q0P|N5WVBT0vlJ6H6Xgft={z%-H?m>1sSb-B3T z6Ce034PR{$f(tYf0QZW1W5b9I>Y@aQ*b>#|y&PAbXW{uN8`jP(+WC%R-PS<$dMArk; zpU#WUzyKoEx7Bg$U=Z#UwEdMFNRV`(dq=_fcrXABPb~m2Upc7bS0K6W+nKj*I6s1! z^6TQ@Vq~M6RN1-XilS!+rhpaylH$HzjRE!)DQ0iGZ-kIV8kVyMMBVQ;v!a2@f$@n% zQVK@Ve3fLNG0>0@8rjhw(9a+WUc~L0tGCYk0qM67V*X%ivSHR8eq}3oe++@z#*U|n zm*I~_l9xVUJT|+bTjAyd`LB)T13gv8W(e z%C*`)Ud?w>s~~4BwV!bTs*S<5zk)vgtnqv`=-&&od`Q>xUtJqO)R)2Qz305c%!&HL z^lEZK6FD_l>3r)DjtsxEsF3Y=GfeEvmEn~@Be%Akoi2xeUvlzOUJMO2Bejg3Fs;wQ z*j&O|F?H_m{K^}|81my)P88d)T9p>30T zX#LmHa1jZ}WeZ1qkh)c^_rf_OG|^Mf7R@345f4C-8lii4q538VDiLY#vqut2p=g-79G* zSO$W*Gk0#kyg(^dCn>J|Pip2`AQTL74Ao6gsH;VkY*Q~GnMo0i?b8w4gQK38&8Mla zs~0RpsJZz+`ga80)3;_Ujo8ByyLX^xziRXkVo@$3B_5RK{{E8>j9_-FNbfwT{0j(u z6ShhT0p^+957N@aa!`ISGn_KBz>^P4MTanxZI`LQ8BJ-N5!^&2iK5ea){hA%Grw|v zY+O#-y}f;uZ@HE9vi0WfhU}Vv3gpJSVCm(-Om#>gPzRiY`P!z;f*}!bZiF^Mg4*6_ zImw~veO_2U|O7|$pU`Kn+O)F$}=W&L4u8bI)-pnMq;hnE@<7uzDUdB$P1;n;R>TbZ1C zPcNg8O+j_*8(baebOVWVq5Ld=Bo`6$x4{e$amiqKz!3CRwDKM%38DzwW)733RBtG~ z0E&uV!09*^o_rVJh5D^WD+1;@ir@ucvI`LNuvk1woMG_}#L7@}*E05b^ayyrlAU{2 zdxrE)8Fl)-i^!bG5TyWkwW(^y?8>}lHALly*9WWbZ_r7KNZaSD0oeU0ZnscQOVfst zpXQ;gZ1_xZ9FCdc7HSosJ#jjRiLzSebcXG0l@WS>5)nEw2h5eS{xRdErA5_K_5%y% zOD;MAO1E;I&Smb+ume^J%ZXH!1wp=IS&WW`=Gch;35X9KFw$ef!LZbN-Rsq|B`8>? z*(zreB1zuCl2dA`n*Vsc4M32JNcqcST0&!)R{s&Jm(96T6x(EXw0v0MBOUJWiE3!T z+o*3wHoe4$=@J##5a!vo=z9KPIY+FWb}y$}=5Cv>6<(6j-lq+!5KRLcAIIOKau`bC zqqBBvX-3h3()#42yQq$FXqIlUM|Gfuc+1D@21DmYIwmSW42ex1L^MW520zSPD(*Y- zNq!a?p(m!3_^TVB7)w;9M}63f^cirFc2-WFB7HIl!A@2)c+R!ehcA#3AP82@1%@%V za?)W$wv@iz(yPD1N3sO%Jn|a025{{<^zRB#|-MBkG>nC}7({gkVY#wK?e7u++ zjVy`F7*Pmqw3}Zgn#6Wy3VaU@FbSA{nj`?hCX1Fpe>--?y+zN}3TE z0;?nMx|3Osz<=Ed%1XD4q7bDMNG2gwQT}^JLzrFzp@ew*H|{CJrlb--+bwzwg5c0n z*(YId3AHPs{@=_*ZJxRS?V(os9-i%y>I8! zGJ9EzjVmdzlLXm|r!e)p+ZS#IGQPQ6iwd3dJ5lU!Nj_M|64O-m^sqJ?Ce*#>>8$xW zUwgh|m18_KF^ETHGklBxOkI3%vROdIQ(-cXAr?N}Rj`68>o0i0CJf)RBg<4l zi?uAp;%;skI-Y=pn2DgLfY(XahJbO}AdU-7;d9?wz(-ktFqb@B1oMhI(>k9bD` zAGU&#;Km8_ng2ROsz5d%wZysX$AlYVa?q@y9dKwHV#&!M7F=mFJI*A50+7!2MKAB| z`G|qh))4cMv|5Yx%cu4BDiwzTXDI|h|15d6QtQdoT_l*U3GZn;uP`1WN1l|e- z(tq;!!ee8g(MN#oqD9)}xwfxtj>fFqXjEnlAJ`&^+D@BaJ{kr=tkpAw^zRDNHLD|_ zcUH#PCNYvWhSP<7aO7@b8p<#+FGLXxoqTX_jE^enpGhxIYhQxsF5=9v<+qsBjzE6A z$w^#>mL>TD_!70865zKGF}ycEgZ`smb_w!erPvY}y%9w2I!=M+zAj3cuAk$(Tf^R2L}63Rhxv*x&eq(Xtmk#M zG+~A6xUcR0CqZq!=0-{T&3NsdfAwS4)o#wvgV7mQ+z$EtK2{m}XvPM-Cdv2&Fj8zk zQ7CUbyF)z0{{5xGbv@JI@@FS+7a&92CgZqI)S4)QLH6{L{#$7FjGfe?dpFZKtNoFyJH_ivu?I z^D|&=E>NIGq5$s6$cWJw&4~eWt}<6Ybs!7`)_!sKfpE6XibSju^a13PRC98+rZH(` z0Pqq5#AO$Lyt*dXRz9_W7|=}~ZT56~*mPTX zOEM0gbOXLj>Q4VY58hkEmb1&h_4fAEx}WS_Xl2}ptmb;H{kXj>eEam9BaCU1x2{fKU$#vTzc2(PB##_=xn?qq z?q)socWpW(KuJO>aqpWFEBDXAHC!a>tOjnin1Y4QPN{&NthMdl2|n6~M~uCBwS7Qu z8WkKcL@GQqZmc~_ue?!0=kL_wmd&4|W*qLhYMn_GWwxd1_ZW1^=$!n+Q&o#C{PO@H znBc%vg)XF>dwKT*5fr?QRK?V{&1Ca${+1lQ)H5g=;GGcb>egDh$g%O4fq_2T&pwuJ za56CFbYy#VsvZCb5Yh25^;iA?KS}WE(zSyFkd0>^LwME(;C=Tdq^E^1U%Lj|e(<=z z@dL)&)?HLK~}xQD_?R*#~yN^im)GIPb!v2JRL%4F0872TpOqqlK^0w6ELS^E}n2}Qa@Y1 z#>4kv4Z_FT&SD($tp|oFw@$P~tr9Sgs8#&lV?GoW`7!4__jv?Z7jrl{G?-3>e0%Qyo92D;_~@W`9|<^vW-v$h!U$pyGFr%1B#btV0wvD0pQ{1GvC_S3Vd} z+*}LfZn}d_0a&fjMcvO7@q6qzV6T7Eq~LRlQY@3WeQvHBfXnFdkTpe|L3AXcoT}J% z3vJEgiyv~3Y_HDn1*r%dh%sWgH`8qN#16JJybTH#w~1*)v2{EldQU5GT23}o-!WYo zgZ&Ns#oFGM>qeG7SIR44076a_Jf!9UW=ZitM-p+`7+GFep7%$DhYyN zj$C%QCirilydoDnf90?0XW>%pkL#gJ>s~;0O_)5y6VbOFg5>BXa@FUcYOIvGJN9c^ z`%iyz=u11k&YLy6%8kEQH-Hw0qWGULbc;Tx-=-i?ja_ONWxmtLoZsy! zbi#uh%tq|dLH`kVY|bL+{dDfvgRVf9OuMYe0SQj>Yz~eLc%t1k$hx|%a5YSD>Y*%c z3VKuY509D+Kca^0u~prNgaF1?Z?uR`j%6ajhp9yo8jvk+1H33B*BNYwvSvsUQavZ| z2znsbBJt~5K$ORpwPwc(-ucK^OmSt!Y#xGce++<{HN@@#MFs#L7l^t4T*vH9s}No93WDq*d;S+zKEMAI zGG5DsgPVznNS^l+b{%6$w_5D_oe`~B+T><1_x5Lt-_JMN@o3Dn>(5L@qoevF-^^03 z0>if$H}6*aeTlmaTmky2hTz za}Wy`jSN^OaW_F(q3O7vdF#%kC}^)0zI05PV|)W)qttUDL;0&$T{D z-RL0}jr}f1ysw{}DXO$AE<BkhYy<2>H28Tr;oxiCue5F(tAMA?MaQu?qRPaJ;pG zzZUB?)ycr7;VPMwK<*yl`<{C&o@a`A!QW~f%`MwN>hJY~FM}p`-hNc_3 zAiQ;CWa;kIwl>~Mf=W(FFwI0CMT=)g8uR|l9qKk%3HbV#;Z#C5aOH^#X9>a2xzvOUc++zz0?j`MN zzHR~gaSc_3W=P)oe3R)U;`{}5(rh+7jezetFq1xr=65qCOTrAFp)>VynT9Ots1q&@ z(>Ijrd|9-kZE)V?24DZ0RV-4X+>?~Sd`n3fs=pCQIrN7}FB=MC^;Acd6iTqs`-Pln zsmQ9N7sV@f!JEbSSc|H#T_-w8pTP6h-sNroQ2g32#g=FJ^1>!;vPpUdB1yd%K^|%? zq1@-C*VuIkb`6Z}hMAOXh3zsMj$?kzD~{N|A~8057tDg6(!ZAws+p2*nWCQ9&jO9B z_~CnLN~MJU)z$AGm=z6-E6H{L=UCiG$0MuGPNA-@(agJta4kIN9rV&D4d zTE>y>cI z<@(LN@AW5gVxIR1@Lp=xm05bsy5it|6Ke>qT=*t-D7fa}3>tHw2(5Hk2tIB%@7t^l zD?2lzvG&@%Wqw!e>NO7s?;dV>xvqFT{J2h)Nm4h(+W>8#Q)1b_hL2rQ*xXD9_x5;# zcBNnX6@?szDv*{gHm?8GW3+cVFn_}3sJtU2Co|xa{kVP#i` z*5eC7$NnY?`lf}J-EMK#lgI^;qto<&2P$*@sdLTMO6~4@&;q-Vh4@;5PN{RuwyeLe z4)rpwd*Ypk45=Jw(2LR(G6i2^d}S3Od|v-~Gp)vHbPbpzs3a0rwR$kPXjXxevd?4#s+>F@IwZn%BR!ZV7;zUgzup zzVwdY-HrbaH_J&eOHtN0g}=QL(QM7OfjwF`SpDfi2NDV}m1m63k+K}T$(BjZ_lGSv z7?1>K9ZwZ`BFb79pP_$4W5u?Ak{wEjPRGty6C(CVYpLdVTvRn1NDO z(Va!_3atj%cxVm!?!Oa;zDcxR2dxKckItB zxDlpl{S&n^8B=vzL2O}?uCqf^HbK)CDUa7g6E3w>f88I@09u`wqN^iH8%S*V!0dl@8Ld| zt0xt9GBqO!)8%dHCz3Cnn8AF-f~>M&F=}+vbhu6(drlrHPv2SA+81k1V?w@nRokkqQ zotr0vc#3{kdAnhqAI|*P)uEjyhWpTof%&gEqd3}drsJqJ#{=&Avqpu%8V^|xVX&0i z!ZODzk|md9Ss_z2)fekdQ-_)A5{S@Tlc+`-zFLkRM1-b7=ms-U2*%T>;)VN{TWkjb zOhqAgrR~-l4oQN_vu(0;a0>Iq*Ry63G&Yumdz6e}J~XGwK2QDQtWbhY&NuI*W?EaL zjhGRLUtZ3dAuvM8^w;dq1Tb*;Ngvj*3+Yo`{Op4oDk#?-6;nS|(R65b@Q5o!m~eki zb@1Y5X&a&6sX*=<_u^FN8}BuDe-KCv9k)>^G%IF+&-ekxEr@UBEg}~yp|iM3BDBbLS@kzZ?_CK-n}rON=oT*$!pJ$xO4cR#$8KTzp|(NCn7l;Le~_m zekGAm9O|z)XhT-ge?7>v(~VY$qm662L1^%otwy*oeK|(o4#-EygIM zrW2h(D4#Jf+^bfeq!5<2@J#P!$GKFb;CRTvm1o3EW)#V9&W#nahQY5JItqC6P8*jv zcFp(9#qn?@s76mQeJO8t;ER8Iu@eSB8g&>=>b|ly0OJ36-tYf-({N_Y{+))qq=IRQBo{cHyX>6+0uXM zU8E@Z*dag&x+Bx`c$6Sup5vgLNP4|IJ7e>iiq0jpzl|G%j3z6U0D= zTx4u&KKm}fq8h9@_J^tk0|AML5?qNSiOPL@o*^)nDzHYB>7Nu5nF@P$Py7PdCv}iW zKWLK$gqJ$iR;HDt*d09gB-=c{?3$;VrPzI2f)4To&vo3cie?}RzHB+^O%DI^Has$o z+A)&1rpGh-YED!&+OE`?OIYpjYhd_lCGoSG(`->Gxoorz3=ahuq_wE&{AG@o;cBbG zgV9o<<$Uwh&-dVoLA&c=XQ!j}2YY`)$*fDPLWqF^SO0H|NVa-2kFctTgAc8Ky$$Zj z2RRa%ufM*n6&SG1;>m9Qe%UFWMBfS5fB2c<8&qf3lSUWpUvc(oa4E~K9|%gLz_-*9;wC8>V~2c`tz%;aBPweA(`>1_kIbp?WjqfwZRZ< zRJITM7Sv?mVC$!!WRSUycJ;q2Tw81n?K8H4x~EnUE<^k%Njl`IfDtzsPxnG-&BpB zW($&jSEuQxQ=0-OQ5U~>E36{xKExS|(ems?yBGUtB2sz9Ji$ghwlB|Vi0+O%tCtt$ zf8M1=e<8Dk(y9OR&RioMuJmtlwm)qiD|QQPl1;!o6vQgY6=pF~z#AGW{WkaCs&%Yg zWa{c4l&d$|w~rbaB#RXa8$FkgAAuQsIWSVmvK#*m7W?RNyZ`CDPOKa1+Oi!$RgtMm zl={xz&)4?nH!WQu+4;vmPq!%y)XefD3bvtyY8!F-oI|PMZKtm6kCKZpMFFrW7F0yB zmu}n&nmFW+?`;s^2G>~81WA*0G`G%>_MuIr6NK;7n@S#E^D6+|y`ou@H)_9HNt7hq z1nJ5&lHqly1ijKTl@{QrH)rGjPC3LjzP1J zmj)w)xnp-|WgH6VaX+TXXdZ*l@kBWYQ(|2DPiN8umTytMHuNz_cAa2h8CnLM=nTyK zYRU#;<6sbFxmMb~dva7Z*Xd5Yncb3XoPf`$z?$VyOZYNJq>9gtLUxC3a zELaC%6Nc<;!xH;HR7;9c=XmUL2(o%X0##y%FgxKMPMI4?=Yd&Or7wl`;YNXjMzFuh z4OWt>xm+t9_VLaAkL~46NOCQ#lMw$yiIKIa_E(9a0P`QyMPY*gzz{+OIkfZVfFtA| zKT;*da>v5%ut?Y;wMUOxf}JRW2Q82i5XObeqF&VS)R(-miEx7REg4&jua24;$3HH1 zfBmZRzUwIRH{xTeDIADsEl%QaoWwRzk;DoNj2l^cZ%(c41rG1Hhn3qvp}3lhIdjor z(pbCMZge(wzSh*b2152!LJLB9;kagAJ5ej@NMYfrzt}>+y&ap=37ZA82QxTw5sULS zAFk^m$!0}if|S(nHz9hidi#AIQpNEv`IklYMz(P~t-u)cY76-U7f!iOI`Jc>6}<~d zc=bEegy&U$fmGFc0xEDMrxAhYN|^7pcda0}nk3721ErhKcWoZ0F!(T-!}oemu~SR4 z@U!4rq(OI^9ZfvuGYTi3;9~b=tx4ipZ$=e$rMqi|l6Exa81jsx!gN`V!c-^k45Ub* zuzqO!h?WeKydApPBpSO^KN(3%KLM2_8V}LBl@VUA+Jl`myY-}x+fusP(s`jq%QlDk9k^oT zDxdSPso5$mx`G$ij(xj(;;I8^Hm#WqG|CP*yxa3KtO&$j13<6Z zRIqv7%-vKFy|*kbFta+nYAa)$cRx|?DibHh^ot5>ZM@a4;i`{m$&T{()4=-h#NMm zkr4P?Zp`fa*HN1rA~l?j%dMO{-|QYVDagi)%-5bUJ3nVL)w@}-lGCiNY@SdcmR#xG zY?+vwz<@6j>WR}aE7r0(Xc#cTSWU!kN54BaTsxaBUu2p6?tK2sfE=d^^PDe!kUhK7 zUA%>CF}%vI*OnAM5PQNyoT+KnyB~VpFW|E-e)v}b=i;;pQypPa5H}4+hAz*HG|Rw9 z4!L0O2ErZb*gTI+^BCq*o1au&dim8hL1hT4)p0}=fQ~>*0hIzA)N)VMt$glP|Wpk5ng`(S{iwUKpgI6Jy)n&b-KH!w1L+U;3 zsm&jrpUtQJwu3G@{R99@OJ#ou2MIi%;ripDP$eg!lbV^tk)VyJ)38%4k74N)EvDJS zIb>1KU6)%1sEImn-M)sLdG2!i{*k`UcdSiIM?=2Je-aMkkOg8>e8z&y*8t2hSHm?H z_rfMGx$F~Btio!~p-=}dceE&r-a(G+w()-kHKZtM<2r3;mjPWLeF^gZY7&2oRM#Rx zqxS(^s!Yw&FsL|+i$i`Mzje|5ii4e8CS!M`R+X=;n3PwQ>lwfjhh*rG3dj?M7*@o` zTzbkJGNgk+Vsocm*VEAmeV%IP?*mp=+#Az!9#=&^Ja74pbiU6u_yvO-ynjgBVzUov zn5$OZkAH0ZmRr}FYY)F!kY)aXIs`EU#>PR4s|Hpj;H9KuQoj|_H~n^igjKFV5* z4muGAk+Q-64D-gJ8)1T~*zg^*d|gSJ)oI`Q9rR!8*=|JCs~wUD@_ZUGCpmNbYb&Ih z@3Mzi>H>^$>aFS+fRKRHZ%;I`tMXP2oi#U{CdM+b=)+EQNGPZ$P!b@m z(f5H*Bg-Ndm~n}0=JUI~63Mo03CVh7QQ6yfuX(n`sW%smw6gMJ1DB*ZK$UNHS3`lX zG6}hYA?M*A3PmI}^7+h->_?Nio&jajQ+k|+RK1e@p0%GKII?I`_-POmSEP^!$ zsWt``c&WRF%8j3Bz=h3eGfvvJg+BVMH$P=}U*w@Q$^LD7G{^g-$0En-$<68Om?Jfl z=n^y)i~)Nd@zeQ6Vs8oIu2Z&Fr%PpafYXGv_>#fm8-{gMIqv9<199_HB}--SH|9T2 z+m5s~?U0pFM#aI$t71sjwBBE)*;9S|f`hz>8>^a<_zn@-2N{prNShb%9KZKj(&g2& z^!AGgSlET(2!(6iYpju`2}1g_Lbc{w7;fGLd4*pJ+}xM_y4ueY%)rMh431#yO-ha` zAEUEDO7~y8jH>o4>HBNL^D40h?D;XW;O$AtKAtzF%-E#qwl0M&2wtb0h$EAYA$oFu z!qA7O5g>fMw*5f=?zi4T~HC-LdjhE}qLOW8siu4t8eny$8YgMrQ^Zg|}ibbxc{N-Abc4{cu z{=o;K;8>@Fj?91ibXdj!P86RpZb>(w{vf(j7PM4qM;H)^%-^2v>1qTQ$JKeHR zAVd{#ps4u}^K1xvI5^KVZF)I{Bo3+C0zhQiu$rk5cF$yBZjoH>J^E4Jnf*=HPNr*{ zF49<_)+IvSegkxb`@2Fy41J5+>@-~H-5+<0t3e-|_m|0UFwQ4Gq&}Zq${C&V=Crv* zE`bP-XIdT!{2UM#6uqx2uVdd|2Tsk0KR!dH71q}c?q`VcxNxqRYB#^kz~AIV9chnC zXnp?=qYMCY?rUFR{|AkAXS39hsiPyP&s6Jt9>-h0&oO5?iUW5HLp`pPe18;*b{#8v zU8qPiI7sNIv{18;j}H_+Ha1j3g|1f9Z9?NoCu!LjQhC{;lgInuwm(|~s0!Xrvd|Dq z^y?ijT}^+KWrjyA=KnU<1ptk7;ytV-MbDMUj$}}HxjjA2GQuf)Zj4S2++e+R%IHeGh7)3F`3rb~%*ZRWxb)~Pp2;s^&G%Q)lB0==~`4!8WD zi)icG+$u@y8(4M!f+@36csEkBIE+2bFZtKdVvU)qPV-D3S=Vx}2G*4qkq%uz~ zQ)Z{rn_a#p6P122t%VoVf)rH9_$G#0GPKOa)#B3yDLoNJPbVppUNv&mls<5!_Eq$( z-t{62f_!$Z__vq3_eWxCrDRE8JYAV~&MrIB4uf5!>(Ux?tsvKuUoCf5lU7_gDoNLG z3h3R4e5I#mcr98{gCtKq@VDLMDWh{pc=uxN)>6k4!|a|!pOIOn*{^wj9|0Fc64=UP zhDc3@?&D+!-!EtU$8%VcxEQR^^YFzFRj&FI#SrxNkHeYvwjmD3rkv3>a~ayIz%!}QQ5Y1Lr>2`ps~TsW;~BoDC{$-`x;y|5lfT@54>Kl6@6g}a zO!VFGd+RicK3V$Jx_mGd5#k@PF{TcDHMtng;wV_Rv~aZ+UsI#T;PNW{3ct%EJsuMl zS?!2bt>{V{NkKHHkFjcMc`#O{h)JxaqT%ahADO7v4f(N{vLU$G&VKNN=-e)BOp zZNeaJH!J)3h^>{~6-KJh9zr!m@AL_I#XG#U=~Rzh{J`7EzI;uy6LiIrv?7@CwnlQO zZS*L%<5FZRQIwGxo%goqh*53vX!$*CRfA1H#IwEcZ5AIx22He{NV*fyxR)9`iKmfX zpSdcxsRp@LreLs!Ipk7xyk~u4iOGrrw&m$gec!0r7-zac5~b#g{Shl|(i4MIP{_MuPdj;Y4$Cx zImg1hz0*J_-5-kPtoh}cyy9Jpe7k62EsevK%uSI%jv+hkchWb)2Z8d4b({U4YG;P& zkg}hsn)xv;tqMo$#wEwA^)FE~0{T!~n*9KZP~D9vtrqvsHRr8;@?=dhe!%i7C@EpD zK^|CE+L%!DNSZaD3iILZhbXR{)`Zwe0!98`6ThR7dSAB)&-_kGkhnLRnXK2$+05Ou zC2F)`$n`f6>6t??lhh%ry)xvhAYjQ4d|E&!y)}PcBMZ1(e#O4+9&lY|bMFuSXibOS zoNTD1Q>co>T>Q&3iqiK|c>Zki?K^O*|4K@r6Z4wM+$EBu+gbUvMzR!qoH=GCLk<+v zQF#TAtgdjZl>qGNNx`fB~q#l7Uz&qyF z*qZ-q#nF$c^WpWr`pbaLuMs%Uj3>%Z3x)JH&?^lAgL zNev8P?w{oqkvc(e1ki+kQbCmh^_&QG#XU1(sBKL=l!i<(iO;Xx@>s8yJ#AA2t?tK* zGf>$!V_T=`x#Zyev;!@=h){&ES1kM?mr~x1?tsJwIl|41e5tib0$uHff)~3@bw74u zQM#h0D0;>v{IAL-)rpIZf6y^e&ue(9*T~c2=%x~_%TwCll>^frf^RsRl;GVD6lG}1 z)eWDJw%L3M4b~E#riRjPgefmc(3e6|KCt`b96aQPLjQKKQ!AhutzBapSf8OTy5wmU zDKw2bVKt-sW4GV9CL|7JCP9+}nz0A%ZaEt5C*5;f9v>C4>{R9S3WuV?%kwMV|Db{L zY>$SyqEPLdrNi=&8tC7#=dImF{N8 z%1^~m|IQ#l5Q6u?IqVJz334?E|~tc_#foct>FEA_<|$M!K?#RW5~ufU(s71bOHI zTJ5VrIzlQQERoP>4>OJYJ_rQh&K2N)A?&J4@qXwFO~PDra3~UopAMFwEL53G-8QQ# zKU|!7Q~75*D+x5YKD_UU1;1JmwZ&u?*R}@--mLdYuNiFB5OhAJ6nSBRH+Hy;((U_f zc95voowK2MImMA+=4_F2TDtUKB%(If&Rt`J?8U2(2e|u&LVurUyqGnD))v=(5LY7z zCrrn_a^{ARMI6v|p>UJR-(V&+Rmy%n>RH!OZDnF`XNoCyTMwb4x;-^xacp;3`~@U3 zPj$|m)zV&t?Gzzq{Ki;q4ihXkPxn-Qz57libZWb?g z=A5XDr=Q2|x5XI`24Ktvlx5VLeJ{njG^_6|I-pL-8&R-ILXKx89z1!g8AiHy23O{* zxpZwmG+rC;q#G;3_(&r456rV-jJ2;1*Tt1)2TuJhyMCFIOBo{Q(gck=*Vs^n?o@mU z@lU%Pso2f|z(KDKao*;e1kBJF&SSQ2LIn}NOBW(MJ`QNed+Uakrn)z{THuZPe$e>7 z&%Rt{@t>3ahf>61gx%Lm!LnT({GXrVHpqx>RnTf%ZHKrKmPusLJ@J@4bBMx}CDW?bQ#F!*x z>{wb**B#xK?Sy>W&>?qO-JNVRf0%%P8p<{mA9cOoU4s&t$d<)8sV8A6q2K62#f2RD zKY*bi<>%$&%<}t&-&}#yRhWvkg#@~2wc$%%*ZV?X!$+A_pb}q@dm)lo-SKd5jltF!QJK)OLfK9KJ2Zlt?Kx*N{;|DF4Db?)|t zwbq_B^UmAPGt1(?GhMXmI2ZE_bFUv`pRXT$2VNdn?ICWb>AZJt&=tkw(^twQ=&s&( z>)D8Qk~=o z29W!M^ACe|E9ia-9A~@Mxcy#T5~E~kx{SA+!&hTD%y*psp|U50cX(!@riwYYw&dk7 zrK9s^OM1~^JEHydU7-&?&r-*<*OjKzhMyAK55(exOz3#uWae0urhTUPW; z;YMb?_+Fr%7VW86q?G%xH$)Ao*oN)Jm&8rlMz%D@lcK**J3r#OVqcM(PBw}4pm|C{ zpJKM8gzQ})IMzvOKCRu>YXs(}=RM#8ptSM4z&G`cBIwS`(};Xt21aX z3OI$y+sPU{y<}e3S$gCVG`}b)dNLvnbnBjvPThO^ylBb_Oy_)?Y7DLO3O=(rG!A7$ zO4b?Y=ZEF?g5Q4oJh8^VyUK}nIqGAWnTEph;1dcJa!az5Oq~{hGeClI>>9yk?k6M- zYZGw$2h|M7AX>qHJM!KT$75>D*ZU zQ74NoCB7U#qF9>vi^t_1atFE<>LNdf3&P3OGLqzpiaY;6!vv|w3|!UTin|gDI+3;W ztM5ZEZmUUM8I?{Swjs;fUuxw#Q)euS+=q}1bUtlTagl2oE*X7fg{I6W-1 z#A1SDYf$Zl%FmYLKF3Fk=VPDBd39L`IH6D&K6)`7@-x-GRS1V|fz`(nhWf+LoRu-S zSnzBTuDkA4xrgN6o8cc0w(NgHl2x{weyywug&74A$`X|up&sK+Cg?bz9Nuxm$_RCO zsoh3RxFfc5++A(BEpW_Ql)aDEnPLA5UUFKJCE5)2{Q)h^UF$@>sbD%n#S2X`N0*Sf zG~4T9qdi~2d%WO~Ga((ljwiX9ao0#Bv822u>y%kJXDLlv)a2HpxAG7XnA7c*mF5Wp z=V_&zlr;#k@W)FS58ON9cMUd;h0uDp&3)@`MTb81Tbc=;6a50eC#ty?xN+Yi17(gz zVxDh>O($6{q=z^VcuZ`oNCJd@pihStQfdc{i z&)mIls`KK!XxYxvZ`S@4V=YRriy{O|nWq?A6WMCnsJXrqv`AD}WxwM8E=q+)owkmQ zf*x0SuNMvS;9{Fs{{Aqf7`&8eEqkMkvvJS}Em+GbGFt zEB-qk-{ZFxTuA>~Q;!%Z74-R*TUBnw@FJx?i@?!sII5;22=FW)E$8LuRCFQ+uHQo> zCY1$7Y}CHGpDeE0iwDIXc3*Rd1(uO}ZAJ8i&8o}lL1NL>EW!>x)518$E)ccC;3U+I zsI9luz|SYGcqwrnpB&dy!u_j5?Wkxp>9X#;b0D4EXrM?DKW458N5~!Ue0@PUbM==; z=1xaAZ9j(hO~vZ^s%5ke6Bq(D5YMcWN==wz;h&7stiJW|*e`j2`ywgU{pF5QLcPga-WVvQqSB4zu?R)N_yGre30)n zI%1I6RfA0AfHpc16e+IG2`mac98@o~ww%u4_!QmROrF9(13dDIURopkG zmu<$*i~HPc!yD))6%g)7#76Aiqe0!=K_u!OyBrcd2z4#adVJNj%Q+4{aqN40*5@8p zn(7BNPOM6f#|9_A;_W|wTVmFa@#}={h>@ld#Twa=G~U7R=1Zw$Ql6^&B^Gh1zZCj9 zPF;>VufUeZE9f7B{n7p4O3MhrVE^>(anJ{cHC|;gPh=&hO@MI&hozs)!|){{saU0Qb83!vaMT81MRb7;Cz8dll)TomX-IXS?6m? z#~!`*8TOGluDhyEY65!U}%y>O;Pc)Ma$0SGoM6fKy6=^U|dR;fUiqh>%C4N zo>p(@#PeJKF_nLXvkhn1eX?AsvnWEQ=}rZAiR%A4MT#73frCU}0h7fpf_>^~=UH#@ z?~SzSt43L}5xPCzXb@;_RPZ$gLxRhluF9n)dGuygj(JfV&+dJf44iQxqUf(jG{~cS zE5ldN{$oRSpOiQ!cCQ#%y6UN)5g74Z1w7)MC;r$bTF~357^ew*oM;MXMQAn1J7Io{vjgAsoJ8>-!mpeg}ynTq!A)!UV)!08d}e*u-shd<3Ub@{@_1$4h=o< zrc|Q4lmhN|WtpH}r+x8U za4=4)p1G-3pMsoCI-pKHmFb~C;j{FTa9n{5XUJ&&dwKAWZuauQk{|5&YBq{nF^9ISG+G@&0rdL{!3&D; z9rHeIR=m)10=6#V&B>srhDin{45;dVsA;($l>U99G9{V_j)3k`XJLkU4(r&f%~{p4 z{j0d2-BH!{Ime;qyzIh^h2)#gGnZ66Dt*=Gg5b2wjGrga)#mkhL}fl4qQ1X|X-X=R zfV#j#Y~sd`ZMwa)iKWC>F)oqjseqzgqc+dJch8-2)vV(ksuEXZL8z;Z@)dxncch&y ze=R*nS$mpgFRz}<$Q#4}17VMhX_&EHgkCwU;M4eIS?T$!?{A^Z)kntAV&clDYdG(E zc=n_{A(ku6odeZRn~0!4*Y5%#iiB|%HY@m2R1FT5tswFHqg$r z{W+y5FexM}<2}ZS>v*#!#}V%f-&sRiTcKFI5V+eZW+KxQ`YXa7v>>}xfL&+cDmS8= zcNIe{dOZCVsqu7M@h8WUFcY~IjHsVQfmqnTvz_=HdyQ_ni3c`=E(g%ECCT9JC<3s? zP$U_ZjY|g2xM2h*9&bCKb0%uBnxT{7TN*Tv)|^iERf7&e9$L&ul$VlC-f@U8A0kJB zSQbK!m15a^E0DtUki99AMGj>!_=gk^CFkFx=-NA*hJ2BPPJ=?udKKp3RkwDX^r>=Y z(C*1X1#lM*8n95n{w!w`3Z-;C+UYi4X1%jkaPSO76{e#}jB1sLEEL;x6yAq3!idAe z>zxsyT6?s=XFj=|Ckdq_0P{`HVkU&ykAn*9F%4X#@$Z>91IDel70UGE;^04^ifFcORS0Ga8dN%f$YRw4PSB92Fk<`XgMJOWuCYGm6 zj~gh_WQ2zTTKF0ffQ0`)KQI!h|GO*7Cf6wPrmsH=LL&R-`c&-2@S)GMgnsKOB_(wxqQH>o`(c;*>UdIp#TL#nT#TdF^zx6 z?10J!M{l?9sx9F%m^RYV}+u|gR*Q5v5sU|@%PJbaAq@IYZ|d8>wi@)fkblB4<@E`QON5K9$3kbR$M)m$o2@qd zP_Zchk6^@|H^-htvW1p?<^uVwes9(ok5h&JJIQ@XgiyAK>W;g=V{MBtPum~^-W4dJ zTPXjNsEL_QGQ*$iVWt6j9;eqze#H68aCl&4`a*qRSqb(ia*oe}2hP?Wd3@@{^N>jX zE%i5sPKJ#`KVodC_Sjo3`SWGWP)horC_+&_1D$ePr>6}R&_75t1NXB@cWU0Fgr5G) zDx@@+Iv@YToT#iH6X;RCD7@)wjv}d_->Vw`ZRO2^hlNYj+&RITg!Tkmbtx;!>oK5-;bQOB`NT3*nl!G*q?S8F??O+n03(a8v?;l|DB+D@I9H`{{UUnQ!^luiyR~=ju;OP08{L(G4DtnD4NmCc)d%!!1^c z?o$jW)DgHSXnL6M0kNxQ4Ru;#Zw3vzE6XgMu40bE2l+sdm(W^@Fow4X&?sgjLC~KF0FpLnDu@F_M0%_0rYqw;LAC;QQ5dlu1ky~m;1W8o9 zO%76L3ex-i2$5Fidu_VV&`*SS7;JZJnl>XtWi@et+hcpY+_VDf3fkuiD5Jd-HU2Sfn)wp%i)AJx|#M6WCte4Vz9L zB-6_DF4GM%7-Mg2eSv+b&jB-E zA`)3hnL#KOK&e|8BK?`oSN-nj2E6v7WJLKs-^ELJQ0ejGX+t{VNP)Cg?vXrn_;c$j;vF8Wu5 z*gNw`u_?vRefnW_TyntO#Z9LB##pZm4el-)i+ z26q8E4dhwi`$89u3PLhYk*I>IdTck_L}2Whfua=!i-L63m+{VDO5_K%t2o>9O=Zpj zzEzj&=8bpHpDyb|l5S$u7`QUqJW7Ay)SP|CZpC(eJf|HL0p9$4U^)5U1&8fptf1Wzc|ozx+O$m$R}0ty(_Aw9 zQF`3=e_PV(WB_&NE`2>r7nQ;2j^Mjx-g>fW*cN+?5bV{vmh_G=9=8*q3?yH`T*NaV zY<^ku9Jd$OmJ^LP>~x?S+=|zx{+dS8jRx;W{EWS(Nwm{I5%u)%<#1Zjj=D<^((yFk z$~W%gRWZN`+OJabFLlY_lc@ZEm9Y3$@?Ij;U)U^}eqSCRhRBJ!Sq zC_60T&0np5r{QC_x##Od=bp`5Ifin`5fY)iYXbY|4G7UOc?#H zWd(Cr?53iwvntQO{%sMWJu$gO3A)j19+E8jqLD?El3B{{jxL%L^f>Mzo*zmY@36WKfx<5BAzUW0D0 z6)(U{-hhoYr5+XNKGc;gTf|UFT37lp$MUl&gsFO@5=AqNF{{|}pMMG2-1jgW=zoMr z^Am9D-rkT$)iko85htlxG|rs$J8Yy!G6lr|6=`#al>ODGAEaw!PLHBrMv?FYIWsr# zv`*T>)I$vxT!25a$ivrtN6KH-YYZeo*#hOt>1r zAZ^K(!Nfudf)~JXIZT^X$+41bp8TPq=r81Z*6%f*)x)G3jxE=BkF?R^aZ=J6MRF9s z%7AUv2WsyVt*MN(n=#L)TY{)4eOeb=$jY@*8RxW7tluqgLKBtM^Td854#IZ)ZZoROJZP{a=2vuWlpnJ6F>W<1h+&~0qb54 z$%CjO*`}l-`R=QW=tyas%W@=cgf6|}B$@1ac6_mN;pGMBzj+at~` zLAj`pVlgjI@6c@F-a>~8Eo1Y3_A_IO%PkGqe2a3PT^O^Y#ilFtqnG_Q4YfB=<)1Fv zA^35YZ@v#6Px%aDQ#~PA%}$bkcFZZ0PXzle7Z}0maO{zlALQB6FC@x+ig;0@hA>=*>Vg@XT7?L#Y1GUGjTKME>>9O>!T*E~biWz7F&uvzwFJXKsp0OCl1%O!DU@_R;;b z>|-l1i;di}%)xRD`kbr8Ke1h{irdvmwe^u1Hm{Jc?+YTu3<=k2^#4{c{L{?r9=K4f zo7Yonp4x;Gq&oW9&DxPhKR2>D)nac)J3@F6>0!dKM-0!AbCDYd?8PtQpNH|hk;O!2 z^>yHHO=G>!_5zgdAQ4+2ctHpz^gw)h-EE+_A8pr*{cGb%cyp|}pCoi<;Njej1{Trd zXGxa$GgUTw}!gl z;T%EP17wIq{7m-u^FLGcFC;aJLAU1V`%9C|D1rIWrIl}ocXGx;=K|%L-Zbo2C&uT< z=CX}RGGc{D;NdVfuy?w^|91us$Uy+hD8$){FpB-<<9$@^4!1QwK~X0fDzM9crk__v z(2&u@Ggo&D1jwh^HnSQMJhND;Y(>o{>iVw@n@?FF+zQ8m z_b-`wODxs-P|JA39gi6t8DyO~EfT?7oL0L|0lDo_Px^F?1L`o0!(<&R9NFct zd}GnqJOp4V7Lq?QWhydF^A*A!h)f4w*b(iniV+}ia&^zSzNP!dch8c38NlC)w3G!h z8*21b^630$bpKoyWFpldLTrG$1%2P5>=Zg&4E14et4M|VO|Pzlo&_KPZsMS)%D#w7 zq1m!xyaG-Gd=q=>qIP!E4VUoykq1IN%y08G%~tidjG>MpjhZw0a+m{L|Ei%e0Y!be zLbZEw)&$K1|7M@`NBAc^eYjuw!8^A^%v?UO_H2xKtw_s3SljaEtN2Br|H+inH(Xv_ zRdrFxVq9DWWMZp+{nowpNa~5CI&hUi2!$1Z{Iyo?_}4Wl#MFykBPM|&G2EUZ7iXxG zK=DH!;38$et?|40jr39lgz`u+@JC>e+;9+Agl*3QqhKkGKiqP#axa}Y+|I9#U?N#o zP@v(T#rH;%o^gtw1TNp|Y`hw7$iq(RJLhj1?g|CSe-YcAxmXA_y$2g1FBe~b@&3J&2h$NbpD=CeJ?KVkM|)VSsD zJ-g)z)z()*P3-db0(%ev0mw!wKo|hO^>v~uuR5nc#=XGY{rq9X@roqFxeztlAg0iT z!Z0<6?~O#{+X#`Pzq*K^@tZcl<|AJs2Fm|yVmrDt^a_^9+ zOs{!we<7bfJnBtJilO6Zt9fO7f>gBK*^R$t$xzm*{Fyn`Q;F9BFRma&|Jj9h1+&@O zT9)hss=cB&h>uKBWtl6+rCUSR4f2Ibnewe19-md#SIafTXFwIahcug>6md@wN2(cG zEtoq&T3X+qA$yu+b_+Y^pdnoC?Qtdszx!+|+j|nE2`Q+_fc4EWM=IZXnlpOn#*eFk zdZ1p8j?AyxeS}EKtLOm0vU>QVK1u~WSP5dm_Y-}f1_UB{g9!JFq2oFD3AE`_rDM3jEih^%01tG*1;Z@T~C~*j!q($p* z`|J5>Pr{Iy*5#{xgJZI37KfXmb>?--bmHZ$zS}Su0jJqY0bQ1B)%);@+!*ff;5(?V zy%~?$pvd(uzxRTKyw_REXWCx8IyCQxeD~Cmv((!ZWCZk6kIL}!o~}0TXoE--of3wS zIE)a4$VO8Dl*KregM<<@Y$msKQ&%(B^}8DF+#Qj05N>?;aNa}&5-Uw#74pHOv}7IF zdhR^pp!VQuj%mqepf6;2Gt(p4(*i(0(n8fxKN`Hv1s7UZvitkD9SNOPYOI#9;e-U{ zm21v6RS5|?Cuon5PjUC}^(}Dlp1)y_;B|T>=Cms;B=7r;nd2v$$rO#F6*8-=p&zLDhh3TR!aKSFk5Gh5WU;isR^~M7PP;8 zv}9fED$3!jZYPn1K~RLqGD{O6-<8490s0I89M^b})DrH0z5q>H92#*J@zAPjcke;T zItA+HEd6TLl<``PmqMU{X(On*MzbH(EgdkDBI0!*Kc=Y zAv6Yve*+_cxTYW7gj7D(r_{T zsr%}2qA4-af-_YraTbI)XvWE_`1j4d&v=fe|S(P;@5vrl5{6su6Ek`C=`qagDv83*wIl`kfJM8^Q4ogT!c0s>KA;K zi6eW~>vZPY)NUt_u8z&<^=Btv`!9maNy3PlvG$@=Yy;HDZ2Y;!MlmRb%QeOpNad{H zC1(4=RGN*zP{L!5PfcPRbWf@@3G{Pxestp1=Em>4Al*;_rFJgaVHeYKQq`>R#IC=| z*q=9hQ)javjf(1QkWTq_zd!%%8At66ug$%Su z2iitqIMbb_uYW2?&?UrM4ln|PFO^27DQxOkI$5E)T{n?((5r}dW1*sPVH`=ansPsZ z+IxUV{5Ke?a?U7AKNFRxyuyGVHat7UIkmYr*(9%0HF)4`Gnf->@!9bf0cA-` zrbSNpu)tj~VJXrPT>I^)ip|y*o?ON;*zt(u%K2LVNvb{Y{b_)P&}8&F@mV8122HjK zeQi>7{!!X{!Kj(sTkYgMMa}tBDCyBe5GrJ-x_@esGWj|196YfC3A*^w5Qfi*_umRk zx`;ZP#6}ke{Kc^*F^yn_(VFR@)#-JrSV!355EB`b!5193Jo@iu-$5V-*N~v;)Tr`( z#DLSIAi`IOAMvDiwJ0-)8K6;qmbgvEtlX#!`$$tBy$iqL<7=~w8lVo#Uf%v_cm<09 z*41VZq3S7X9f2N-;iph`i$Fy`KTYDV&EJl6?vGk13mr3}rafh;I%Q|e0dtsFM(12X z_9ptd2M&hrCyLa>>s3$O6ii5=@Yh^T2SvD@fr5N%wOpw#&~`w@*kE0ioZ9#i$06&^ z%jq~|I=;Scb}F1*JMqFCj@*hk1%^|4hWrjnoTsjlg&-`~+dd|Wg@`7r zt_J6u-=)oG%#CKFh0V6kT1u7=qsWb9u9C$MvkJIP*!Gu{ekG zzpk<}KHYd*J)8-l3SEzAC@B#9=1^B5arM$j)Pw@~mWP+`u6tE8*L!q(^2`GI%e>EH z3Xi9&9vi*CfSCMHsNYq`bMvsrdB)yMDQ;o$tJmtK~nT zt;TA#W@kxP()zL*klL}aF*XQt>69(FHA)Cz2{9phXz_Qy-8p@BT1%gAznDZjlCEY+oTV&j=z%NZ`~kLt++^q47WGWZ%Nm# zw=Jo*e4aO&bO=s6jbuA)NAg7=`r{RbA|l7@^Ui%m)VrR~3eWFU0LQJDQ~zm((DTzSzaOnPKY`>nZz8PhH{`PI+!-LDgmFWU!`t(7)pkr^} zSe{7EZZwWnOTAf(3u8pteQ(+MtpfSfz#X_a&GzrorD{T4wTaTN#Ky298h{bp6<*_U zbpkDCOM3Hf?rj}8YiUte>v00c{fHfDf*6xu|29A$cU85q9P*4IJI`bpd0kC8%(Pm? zV~6QluwAwzsoA09#1=F;{tCEe;y5gq#SfzZ@IP6(SuzGuS?q1Zo4dUp9yA_rH;dt$dlTEx2EfZ^8B_mWbpM=l~Kwi|Otw3l* z%??eia_jncl;27<;acek@$2pFEuR11*h34l3W(E_$7gGz?G_>D{c9fK`$g}T>+(NW zM`!Z}yV0ZdpDRX(HkuuH=Q#NzTDf?ttD!d(Xfr=4PSP5{YHtth*~KAY4V0nRy# zQwc3!U8ld6)(y|GhwRM;4U+3Vr(>aSWGB|5LRa3zIbs<41@;0OFe$8r7rfJ?Je47`A*|JR5x% zQ4}ngHT^`9k#7A@s@=nVk%{Wm|5<$ zYQR?_QoZ+)8XxB91fF>3E>gPY+wCM_6wX-K8FR@-yZlHKM6XF`w&NR2aVg>jhU{)G z^yJYPi`j88o{j62?NkPo9b6cGe94BV-OD$4&8jT2V_#;1Y2nbTcDjm%UmYqA5wCqH zzfd)ls8sJ}9SyhDG7kyZ)2XR8R}_8NQzTh%p8mS&+df6`=Elz)gx<6G_M2T9~xR?YdgrOl*n7aU6=`N%*7n zE>)~^O9Pu>#Z1nQXz{kV$1BMIu~*e~)&)mS`-++MEMzg+x@T7|7N&J+xc1JQ|9HML2=ut>4n5kDJePRWqCfkWV3G(E zEVhiG3cm~<{{S>8t5Y_}&@J_vN6S`-+H9mc)n|Pkve|(ztHXQ#hrT@SN=u4!%!%Uz zZY$!fFEb|a=LLF)4ewJn#eoog5Y4zb1wzE60>W26?*)4f{;zOKXKKv0u3Vt8iw3qS zE)F|+R6*GwgGeJ_TlVE)?gM$d`^BU|vle6GMog%~;DXO<;^z1F1AMDfhhQ8H3rA~2 z@?G+@hMUomsElTHh{#tZf3Ak(hA;SHGY%SWe`6~xjc2SsA68oqB54!%c0w|PIP1RL zK{}H_f}wG<{s{jcw~}28&vnqE=IOs+B@hd9^O#!S|3hq&e2hXl)BRzZiLlRUqEUy; z5Ik(aZlgC@D!*|xlUX=C&gPdC?Mrobwjc($-=F2ye12{3AF7JwL?z&d-~87|$2E7a z#B6omtwBbtiHm%Evz&Z687TIz*&N^vT>ovW(aMdmw7fFD9NYKhunlc=2^Vg1V!@sh z)V!XfGED>x@Ot4U;UsU{Q{R0VN>uHBRttaKZu=hgMEj0^(x|&gwBiDrQ5FSI0`5(@ z>$q7XNI>cU-N7~|m#l%Ln&rP9ME8`!y|2}c?`nVgH;muXFozc$$HfM{@tY)2x)|{H zF|5psr1D#KB1;0p5fViiL0YLNM&_A7-p^F7-)LsL%tngVmj&(^JsLo3Es+rZ^RyUe4u_~uZ6NiQ3>>RBK0eLP}AR6 zL$drC&+PM|Ke_Jh*674^C}u#2!l@>+2LlGBZ$;?U?t9zOd!=nH)R=xS+!wJ=-@Zv)U!-g>%r9R%u5n5c?Q?VYl+>$l3 zgH}`7&}p}oCHs$VGiOC1l4?(*Oh)nlWqA~RaE&>KZBZlmV0B;KQzRo$Vjc&rdX`}aM|_w{f~#|mX5bDlR1f``7W8cjYhCEa(#tF&tZQ8ONYOMW8(nc z>BDK7=8~5gQ5a8J+qqMaC7^LA?eD~*mF(M3*K0sfxLiu%`rUZ6xCzK)#6Qwbx!QNj zBfdT@D0~Bs45#j~`>-6!-Q6+Hg5RB~wyiXt%(7_ay}=pWtFf_6#IEKYvH&_v zPlvw_MB6gyN(R0!X`GGKH-iWL_TvT(kB0|rpXMp%s28h-%pX4y&s@C~fR_xqu;UL6 z4RW~pNRgtfaX76^$LG4zRY&1T+ubA?G813_JG1hy-*wvUN`b*Png*0OV+-b0rVc|M zSoZYnIKcP-V4Zxs6LLHn_qx;UA=o3RslW?-4|cFACnkmTXP22jx_K%Z-_s&!6)xQp zBO9r)!uuaMN>`(WUq(KbrVZ}fDr!DmZM&~*@v<(v-$y{xAoP~~s#cN)WwVV0F*B8#?0cq=CpgEjpLb9qbCzyj-lMgg zVQ$jPB6s5?GI-IKSu<&elcLf#e4GX=HnZG*c3qxq`fw1SZ~Z&$PN!~*7h6lQ5Yl~G zU$ufLlY^hRp<0nWCZ(w?ei&4Q9(DuxIKZ);*mGqEWU2z(-@epyOA4vI4dwtKz*}g1 z^++$X7C>G$lAd69Q?F}M{&qPlW$v@Fo~`rpiT|jXR_I&s!=A)ZJ3~*7ZB*Sa%1k)B z1J&VvU#S@rO=4t1es@KJuK;OXR-oYjxF}HZx?}SCGWv+x)6Vx=1j~BwdihTm+*~h1 z1#E7Qw&D%HEkMQ7`P0Gi2>Y?p8vxX)X4_`t9ik8#;M$ah!ZT))BXVE)UsQ!+Big@t!x`8{fRPoQ!g0z$Ezf zWPY}Rele(0CWj@9|1)owBMo`0*=Waa&VDsZt&Bgr^D(Tn^a670OzUq*&34*EYBw5{ zl?$+Jh9_3d)VjC6*U-KA4*t2@l0o`uA6sK&V7^*i&UCQY6au&sg--xxbKP1|J0{eq zh915Mo{VAhWvHjU`o7bVUprvSQUP}U`e1l3m`>GH?+_`CAHZoVUu8kE>z&4ka6tTwCdS58)(|u3Z(YU>J-k?M+%W^!`F7R zQrTmMKXi;2}{vENQ7k(Ar%y;HJD{n$9~zP?iX3V6-7FHPT38d2~XYi;b0Ma#j5Wj7_* zE{fE$y;I>P>`zLF_icWfzc@xz^U_nmu_8ARL$aMOY;6qKld#@T1>95FcP8Br=A1{b ze74Pms}+wnbcY$4pFj1Hms%B+@upWF>O}&$x7-^ACC@Q!@tK>K{T26E8c2(`EUOR1 zZusoETw1wzS|jY+7Yi4BkXk#)bG)$hYcekEy$2fB&tQGLGq!ZInPq->{s^f~;X_ez z$(8=#q*C%*Nemk1fm7=1+AY}>;QslwSoXBn>aI&WkI$@kSYQL1D8+kM8i$@r{dFCn zF_qYLiTKz`Z(8x?mV>quI(^6kjdvC>?a8i>4cGC#Tv)VQ?d%`KUZQ_ckYx!agn3x& zt$b7Lh{f`4R$k8(n!hK-L3sW14~?8zPYege5b8W|7pPRcRd!lEk0?RJsK`UdtUF&= zMB!^TU`H_bSOPepUa7H%l@2K6Tq}9V*q{R8WN0VRJR-FCG;7CV{^?GN&(IrH2W7#OZr@ViPG* zpHyY<*9hmIo9>BRIn*T0_O~z$sIqID%szM6%st4%hsICpV`AuU2WJ+@S#2^U?Ly>M z>{&r@H??Wgh>dVf4=(0&`Hux&T`vnP{+unJ2fH$9KD(rTbfHULYiZFqQ$;}Hq($6# zJL$cOiV(^4ublSghtEZs7xhf{-OoIR4O9f;vdGgmSoKuFk*7V3BMrv_D#U}Ij!FAm zX=(;Ap7dr*eoMyd$He7rHYQjO+Rk@nGSP&UdzR}dnC>`KRJZOVDIhA2THD7*+tOC_ zO}ph*^1ilT)lb&7^tY4x>BJMtukzlUp_3XMH-xTADP01_-8f4VnYNRxXg-zsUSGAa z!lCMsn!|;ICO)QQRj0?sz*F;wc2>V#6~BjzVeaP`Dw>*p3{b(ADy>+;q$`vs)4iWA zwQ+k6H{V%rO`A79SV*wU3xkyW+D(eUMbaIxiA+3foL+srsUO7`o#zxS4w5eQH$dvj zQ*-s%y{#yo`fo%H8{OVjFLEM_ep<|k8uxLfV&A4@8prE;RY`hSB(WV=YbQ&rZ8slP-Zw!D{#)vcCRZcKxEDW{o|yJiMuPy~5+U z@I~$iGQ}ikSC^XWD8U+C@Lbl9ceZv5PJgJi9Vj2vJ`GM91J`(Gqzztw_ot1d3>_rg zY#HN%oV8;3s}Vj0O?55(60XwHK@2U*JJ0vYk~Bon_QLxD>HTT&k|zqW_#9=`#@};p z{T*kyPtti)e{jX5Jn?{zu|Lqg*xWb>ru?i=BsB2xkk~_G{U04B-1$~Z*<^)6>Q^{t z(Ml^r|M)bUzVX;;`P|J_HL9{h8vPH-qq&bDpzduEBDOW1?HPqS>3UIwGveyoTzdBXxMgy}_Gc020{V5SZ+2<@6t5O>Z$G04%|Yaj&d-QwO~bkAx+cG+Al zE50Vlw-Tjeda_gLO8Z5dX?hyr?N4%HMNp?jZNY%3Hbu~a2#+#y2Ftj6ScIL-jIeD2=5NAmzbnHt&k)xr&z+ue+J10|E}o!@(YU z$90CUAq2mFZ~&&nvwgFSh%k8r82*dMD%j=<k2B*w46+OPPpJ;`1KPmws0yWr2;ylS3+g%TW3ReVr#}E({D3NSZZ|NH~b< z`Bq&>H6!!V-&)^}c%X$KsLqCdfvPIVdLfhjW$68dD;YAQ8qAekOE}XZaN6V-aeC6Y zWvOWZ7)+;>Pa@4YO@|Mto@9pjaug6_XMWZGIXaj6&3TtIKmWl@y6_a7T&p0{@_n8A zPEN6@^?04bN-|pmsf@qri^UoRBZ@pu)`;!xll6LW*8UTrzBOn_Wphdgj zf>4?B<;l|>TSQ3@{&AhS7Wb*k10KBxWQIl<@Zgs%ed!A*FKkCGf(TIfzds8Huds)J zuMohG)yQ7HJ)1R($W8w71IFEV^4oG7#fu8^)S*ZJClYZMY}vE%n9~0afoB5x25+&r zpCZ44Y^u1^6g;XSpIyT-VOfngGz`dEpYbTQcJFj0r93CayWi& zg_GaqgO`CWDDO^62udM_?D9_aW!zeu9ZyHT-u03&GhOaW z6^5!jpZMR4Q4Ka*L!8;l?5ce#Zb)faxn1#{hv%%@a8y2T zY{ryjMo=8z@L*qe=dX7ieX7B!pycIr1vPEufV9-ARnUZIge@KO$!sxsc}vDyrRan&UjzR2VpL~^%g7<) zMRA^U?o^<^XGV%WS4S(otR;~Szbq@OG&|G==r0CNUiDp7^*k?aeGH0RYyX$K5}afc zpQD>|J!9lU)8bs) z2&;`QOsu>dWXRu){UW~PUzV)qI!5R;BzHqv>dB@7+gc6`5lv@a8#iE$XY}1+Z!Dk3 zd)l`teJz*LI-j5RBp+}jTr5TfEF>n{h%Mo`pX9T(+GEJfLKAEA#x%Sw z6*aeERGnBMnPv9vk9F>Dg>|+`hq9k;$~VF%Ds~Y4eRaai@wi=Ij_-F{uaXAXRWZ)N z;TOJHCHpr7jo5(2F%~wGAhoGU^91vJ?+HuxY;OQilkfeD3~Ub4nZ%pNBb#Y;8PvAM z;-;e$esjh|274(lyIj9jI&OcH;CXuDNk3!JoVI7FAl{&5f*A7GWXP1ioHhQl8;kjA zm3DsKZ~2a*^7CN<0S1(CUY}+P2()ZrXnbHf(~q4S%tlHuf-va1Sr$DnYftTzEr3Ag zsR_go-H>+OzVj|8m(_FrxH}(92LWXM;{q+;9OirLvsCn=8yZMWmA{RDC0`(oFFLrG zQEGi4VQhk*DC4<1)83P?rS;q(eIX_f{?z9 z8)2gH9{Pp17q?kC19R6hb|-`?+MZ-$!4K(Ad=<8$^Px9A`zij`9;Z=ss=%jYvlDr< zWrcRj%79dLy}^)B9~{5c4bP=;-X6;&uxs8C?`)ZFco~S}{~DMf^~w;xb!xqSPUJ?) zmo;spKiVPK5hN`9VmK_2Xeo9UBiwc$>LXYIU}#X(TZW6+;uGtE4j)`+@97dZ6i_lS zNM8LxUFRgyBPz(;CGWh2hL}M3U7KD^!Ii{)lDpdnM??3r&+xv|3!Xby_*4u|#0Xh> zQc6l7gNzSY?6``zyke;i*4q-pG_^mr;A4J3&rW^B2=-2%DOCGwjZDs~C9vkUqS$nz z)kt*z;h`e1sOw~myQhEWp}ENwq`oVLh4F1srL2j<7xJrI)x>Q2_lg zhq|NLYoxX;DwRCJf*=jXqU8c-`|V2g8$78VhDPl(K$+JH_#U@w2GAeWr8+nmXv7&u z7Cm=d`lF-?nmi zAgn&s07QKoCZtWV$Bn6BKpOkMrqJTR0Jf;<3LAz`h(BuUHNFUIlb}DYUr+~s>~MOV zf4w;wS}(U(bQ#Q9EMXh1O7H)iHfycRz^6SU4XQSzZbRzxJSi2+&bEq1)E)Z+AG0u6 zC6N~D6u9R92XTvU5L@j2k2GA!lE}+!;WsiN#9szPQD_Fy**0RzUsgq|R1z-|U_fqV z`RT8|I`hkO=iL?LQv(+08NJQ%oCnY&W=wNG$A??R_TVT z?c0szAq5?%Y~u3^&PSXekJS1zH%8Pp0sZltf1{cab%k5f`;Q;&uP08@ICnajm+fZK zLTKT{ko;c$TDLzso^} zH~q^lM#a*FKCbv&1!5R)mNhqD0PDiqQ)#Di&8?o|?yisRNA&b5ln0tEKQ`sa$p=Q2 zx&>=s#jPK@(B2uv)s$V{xe&6207eCG7bn9%2;+u$x7Y2ouXV(0HO(^sSSJz{*oloV zuGZm3IyOUC+}~~c6urmB&4vww64%#!_NFUnFCuXjGuwZxG1wH!t1PwInhGNk{}u3- z%~9;#^n1rWI`(vGF_Fy%i$IQ*7MGc*#o~Rx9Cr7YUko}>sMn6aqe|wBl2POGSJeXj zH{oUW%OHcM?HB;^Cp`r|Vg)tWtLk+$d_hk9`EF3SR|CMvGK3hkMQzyq_((vn8+d)Z z80F(bMFK87m@&O~YF?F=vbo#r^0jZ=Y}3ufp{waUu=)jCQ0TaK+WfXj+~(bvUgqE^ z9++f09^msG7m&G@opaM|B(Wc|aj(peGh*QBu&G)wiTBdWuid&hKLEalz86i0NezZ3 zSn(+tZK_l5dbQ6#A#*0#HTzM1F%Dy?7okL-fSD4&NitmJVYwx_8Ys#JdsH-ilOKGK zI|T_K;blw$hHts4Z{v^rdUybw*9CM_PzaVANjxmUUJ~`MA>2_kBfTEWH?IDy-&3Z zYW7=6U-cr%-{oj74{k-j0m7-o`=m|9-}qr6kNpp;HiwUjjKk;N1Q=5P!IJ8!lwA&K zyJ~Q2p5nrVOC0HeFodKltLsJ_u|G+Y=Uq)eolH+&?X>Y-TjYg;M~}bN(9ZL!ofDTP zC9eP7tGj zctXvWbxBDAyZxzZO^JN%34sUgpE)1>I8xzB8U?5ycCRrXgG<6M4tNYO06J%b%#>U$sTBLv};6HY7+bdhR^*f9s`zuHJ-ttQjbPgY?-uD=6cQCLyH7M9Tmzcs#x04)wVAK z>tA4QB9Bf=4gl@P~6GF2L<;xDr9;7{aaJ7URe(y3&=`3)3ABi=vq%A%()>Kqx> z8u=k|it_WSy92>@%8k;&vn~NZ6MRx#&mg-`$Ya8FWI5Rh+Tvj9l*ozeQH)9a?d2~G za?JYmzvl>l_wL2r7@F>n{cN1^b-vT!+{?KWc$09UGnsy!02*c1TY%%_2l+?*vHv&Z zlb9NILveQ-fkHf$n^?2MUmIDN+dE0}jE@D74uEUAdUrDI!rtX(wYn4fb06zSJx#Ey z=>Q znCHics@gBQvpfS|NGUNS|COh_HwHXNp8HjrlPa|2*B3;bCSMg%t7zRQ1cRoEiWk@C z&7%(Amc-XGyg~jvw=A%m-R1J%zGhcylIf$<^u7EVwi18PdYQE$xco2+B$K`N7a#$L zp}d3%=sB)heb_GMlF5Cd`W|_+c(i0eYy65QQ~uMZG0a~7=SAMDkbUFHGW+f}F>i_a z-yMR{qSaej>EuDGY{tx%8j6w;!GAo|uRifQ!p|+66Bx}G`T>fT5`Z3prt6%Dio)E0 zoygz^Nfgq}p?%ZmXEHuxVS@3;Y$3eUmw`@q3=)Pl&X8N(Bf%$ZNtChA`S`C2kp3$V zR~+W$9edO-|FWO8B4DUrstFW`&q%v8GaJu0kJunoMVR>f3OOs5;8ufNU;#h7Z70c^ z?$4ItU+v~|&W|oCITaN$DS!4^^8>vq1Sp^URgx3A&iioyV~C`~w)Dg#4Bgd8(0#)E zaB&S^DqRDYKdzWEHzwq^ObAb$g4^p}9348@*ih4(7n6d^T-#ghrU?DFX&Pv@xS1Sz zh4%cMd>I@J(3+7Q2jcF$BIv?#N)ED5@5VKij;o4fVv4aU1Eo>8jj=0T<+QJ%gjK7T zGAh$&hxI?F@CGOGRD`wd-7&Aa$7-_;$Hty;dwdS1<-)iN$gtL_qRheWBPI&;J3u1sS3%9a#x-I~DU($s79KYH zrx&Iy4I=}NJ1V3RPegcvLDVHmPRHf`6^AFERQa`LOeEm z6Ms(9n+7Bn1Kry8J-lmc&e_i*jz-M;Jb!=HRjaB;$RPfQY1x5L0O(?EcM(`e z!|U%lVCGVjf6x1B`hzqIsciz)T^W#e!-IiI_(3*zUiOy-tn0BT#)uoX5DTl-DyoOP z?}x$WdvGtciSYN%3$MGLXQpISP-F7NMg)#O`ahT|8wZqdIi5JVoGPi{g=2KOubwZi zw=sZk25<9PfvMCR>a$9dN++pX5GkH*c&*dElIiDxZ*?4`0^_J200R3Vl~70Hp{YLU zlN+zrQ#vpja~pD9y6S9nkN^Ar`t!UdEGXI8MZb-Wd%RR=akei(zvV&6sNJAC_LLLli1gf_te z(D3fA=Y(|KAeZyvXH$%9c*Gl`2o+!gnjmasj7SgPJ_FwVTpd1r`Cb7D?B!0;oKRl3 ze-n*(i9g-wzL4?*l@;cD22{p)lQLvCX;1-1XJMUb-`_mZzKl{NRXG&%T6I`W#OCqbvhU)(67F8NVx1O+#IKgJ(YK5kLq zl{l@01iCEMF{9~8`v-Z1J~CoIY5+B0tB6yw!&PRq>~E!Qxcpu{HZK_uvjXxeM9x;4 zn<#y&Q)+B%30RvbgaiK4+wJ4h%=>iQ9Ia+YM38(v;v9%rx5Rvl!BbPYR^+Fb_8am( zcjdKL8oT_@AIR&lTTz)@v>x96WTdrA`0;Z4r7u@n>_@T5bJ zUWg{hsMF~ITE9(1a`7hzw|dfD6UlZprm$h)>1zs~wOySqq)uBR+xqQ@+M?kMAKAo~ zi2yj*A4-b>J?4+ZpAuK2thn4~4@_?%b^{UFr)bkYKN(XbWKsSrTYG*>A_(qhGNq7$ za$!Cn-2$#46HOa}p##=~Y25~NUB!=lwi~?wyzkULmj0Q)WES|yqQaIlLD%Z=+sQGs9rnZto^2aolirlozQPA67Eqw0E6#jW z(tx?$-&LHSRv#I42o1igcFJc?+!8tMLK<=F7;-=QO4J*1vkh2Lud9P$gwnvN-pcOL z({Uaz)=+fL!Lu21h^nyfgc3R`SwtY^4)aDU@}kB3s@dx4OiC3L=$f6Cw;1E-&&+X_ z0mT$KNlE0J{pAV+b$a!@`*u87e)2xx=6p&NLlYtO(%w?$b{Vz=&{PST5J?F8;J8rL!UIYIE#IOIApW|v{ zA-&lDJCkX?^kSF37>(KduUf8Uq!a03y^r}?o7$2BP(`EQr;B+wn#GlA_x!ZehMO;O z`cn8gau2A3@^)$hPEz;iG$ZZlIGdN$f3Sh#C2|@APgN$7(A4lyn(i&bUV!K+JMCeK zw4-=;`gv8%*!{XUhtgVR>7)0jh>9=bs3ZAY78)8j{dLBUyFKY;hd>bMCLT0!t*M1o zEa&GFw=uOu45{n}csP~sk`_o$=sWwy0G^hjA_Uc1uEwdlVFwW4HZS8bkdEb@yMFYe z{eO~0o>Npjw3r61GgqLem~ga%arDpk+n8yqE2o_-iaz5VbnYmPx$)6IpQ#5Qa z0Q#gzrJi(X7tpvQ@Z*v95S$G_E1c$t+CD33Iqx)&pJElM#B zx+=yN;B5}Jf9ONDJzI*d#D801Qgo=uOAY7>=MTE>{~ev@JsswO&)^&Kf_g3tVX=9n zXrB5lo>Ku|yFnCK=R0exp_aW3`sUG!+?onQUY(W|hNn?y{Uhfcs4s^7YG=G!?m0p( zN@$@RFZTE7MHLG#C+>jZ=-r(#t5ul!^4oxCR#nmUhSsdrw%OnxkW`%71IvrQ= zrQJxHs=N0-lX1)SiJA1dzQRyNK%qWu8p3yVrurbuH5y&AVHZ&46l+Rk0Q$$k0PO$L zoi>gM=||Jq>#-#PulaN0#%z9QX|x-~CDu;gd)0?avCQc~j-73AEYjh5&vo$1astnp+i$oBHJIF;u9^Sfs!& zLlP)_rKzsKdS(Kr$aK(H5fJv54UxJ6bW)jYaK?~AC({c5-f z1i-cP7aZSk7ze(&X^#CZZoJwntPjYR^054bdGtbv9#H0Ky2QQFVd@T8D;`5TzbRO& z&;njbM>`2)q|Kcl5*OpOS70gR!uokrhbFGr)V>eLPXCh1RvR58D`Fy)S?xvOk$1)K zpx28oND}y=;nou7iL#bqh*w9O z{$b|Oy(9Xq3JHAI$D2w4cC8|1JGBNn64y_kIz*#JArS^9hDlS0AV_xhZ6~N9S7gdN z?1dRUJ@6(uB2W0@h#e+NTw%KHqE7|=UdPex(Pa`l@v9?7R>kWtLkm_Cj|DTmax1;a zE4<&~hc0OKU))?ngB%B}dwx-NQ3}+^8JzO8g$M-STpXmd+|&I+?H*h)7E*_$gtD4s zVlNBd42TC=&r-bfc>5b@D#O(9(#du3++RoV+dpA0eCT?qoIz+Y8FQY)B)=*-{-dZW zjAU3DDMm|8g9X6C@ZiTz0!3#>=BWg%3C;P#Dgl0b5$ojg-6wU_Y46QpSAUpPziI2r zK^wpOK9I+9vSI$$UONtZhi^3^a60{Dp`UcY0Y~?Cct=WHth+?5J4jMH+E!19DkYnB z1S5!@? zj;n)e+c=;$tI^h(9qca2R)i6(2HD=5(I2RzTYu4Tb)ap+?Xt562QLKx;dCY3cp&x3 zZ`)?8j+fLEi2?igc_;cf(p`fsM#LVaEJ?OX7D_1$o3jn(1Mu=ms&L4=<8H5^%+mQB zoDo++5#%JCq$G_v>7`XvfVvOJyB3}W5ckoXw+Z1GZRrH2e5w83Mhuu`TfR=T0&z## zLYw2g?n^#EvIP_>Osh!b{~hWluzfq}>7owWPvPvvmo6yV@65Ir_L{RR9jjwMCm2Mn zJ(}Wq5j^U>CJeBUH}}j#Vm=F30ZE@pd$wkd=>lOD)l4#AiTRhD9!!YqYc|XU?2CdU zpma{Q)geBgIrD$0We+>N4;QysD65xkx}iCp(~PqI#l#93&n-$(n^6dR@x!{y*?NG~ z0$yBqbutxISVHE?p=b(h`8$)&LZ}MIX`O|#pPHzR@$sW8MDRv<*)1~dqm`uJqZK#* zC*(ZuPNKCSNx6!-!Fdw`PDzU1u*)rbbWz^zLD4QD6VzVC_{v~5+26^wR5M^Z@&dwl-IDb1%s0j_JkAEkCxwGvPR+!zo)kx?@v4N%QqNvQs?9#cqn?yTb(WF$%d zHn06fa`1f%v5M5@_f(Nm`&imH`+-yBda%b%0k zdtdLWsTLsY^H$6G$F@2$*E4_Nm>ua1=t98h&EJVP4&D?erc_E*mz!{zL>$u~-?s=p z1O>3S-nrF<{)V$+v0Ds^ZGC?ubw|+XAabH->YVd(W2_4LQ`BdtDTUTJI!B48wS zCS>T*p|}bOR{Eb7wvDt2cVdYS_1@^4{PY{3b`ul6H+l|U3OQLcraLMZ*UG{n&Uz1O z(DOK>n?lahk{BEv((qY}lTty+Vaxf+BSXUx^bRn)cob%*GP^6}eXRHViD$ta4(00} ztQ)fqs*JIJYe`2TtR8Z^)YuNr8wyZF6^YjZM5Vhrh`1{ICBXu00aIv#a-Mnc9wX%Q z@E#_Qyhou{CtG!QElY^ELNRk`b!v(aX1~#-!BeSsiYjo2ylir=Ar|`(Fr#7TYL>9s z3V!vs|Mh^}^Il=|G%-Y3iDH^pP{SDVCpQxI)C^2NG64xlt=;oV>_L%q{MgbmcT5%f z!6Dsu86n>Gq6|E_KzDOgz@=EMbgQQ0H|gy%)+04n{*%YT@1QiSI8&b|!wC7$4eXm+ zB^_J)^l+~Bcg(S-QK5BQw^T7Q^*mP~~RrQ^U@ie@}syv-IP-hF(h0H}}kf+Lf*$K&q(3ohY`#L%lX(LZ9~SpWE7uTl%>El*-fAq*<Wfl@hyW{0-EPeU*N>dM}A3cALJvrH;J+ z2b7k+IYEi=Q6SS{++PqN^?ix^9|M%cR|KXw{)7G#_e;k3HsF%|L^F=qlw1CUYK@LA zaMXG{)D@Ia>Y`fg+mwPYBY7mIGXlrsWByzPAE{&4N+9`UFA;vwzcXV(&WYUc0Ysj8 zfsP#Os;6dpxTAG`nDX8Xf`x$iNSb$di(v&JBL(;@=rKm|As2G%vkWoU&hY911^&4S z(h3Zrt-^Dj5LvOWo1I*^(F|<1Ku=bxp@yF^U|sUtXYkj?FS)2o=V~+6#gOW4lOuMHGqH0Z5<7Uk!WU>eTSgX*-eaAMCdpUUvf=|W=-)lbO-j_{ zQb7R}uZ4o7#kw-@4A3szhtX(rfx-t00`6CBJqjQ4e-iC!E(nOPoHlY{2oit29KMX= zQ}T9cBoP$VoAyy@+*+d)u_ZF-erV7PFYX2`9itg|#lN_Sm2!&xhtj-s){r%L9ivOq zo!~tsCoS+1XXLc1Hio-(^bw(DVx@eqN?I#BVApiTI9ddsp7kpuQRSn}Aj%)k_hOF0 zPz?(|;Lb3rc=K&2(%JmfJDXz}Xq^u=*W;$vZxoDoeq#w%LGHf$Jq^G|lUm(y-MZMn zNxxbN7JKKL@3;;Wxp(0!Ew10dJMH(L4tYjuC=gH5)S8U;3&6-h%D?GsOCuyz;y*@+ ztgeKA-V>eyp*11X;Sn=98rveNGIisf62v6WXg`{&j(eG(vqt3*Dh`_0zNuE8IpiF$ zcm4_X^ug_k!3elaJrX{{%6|KK)c7)Yr$t7S%p~puI*?sJOVZz=-k&z0WWL25Lye#1 z*Px1N-q)VS-wc3oJj$r^ej2JFu^KaMC3;xZiKlc2Zqp;PGf_{9G)`5ZW9KmY7vp9U zz`2-w4q2L39q;N>Ls>D%Fk4E&Sq`u#iMg1@obMVjA8FT_(^?kH1C(k{o6njN&ze)- zxRv)ox{{~S7T}vl0ph9hoCrOe;T>aiDd-P+cG;_{zxLM0ny6Y(0UzTIH-ubF!TrLK zz8xUVlUnV*-;d{0o^KJ>Yn@2%7=T8h0*SZ1}#F_ZzDqD2Kg(mZOjYo_Og8D&QyxK^5 z+6?wq#C7vdk@)S=f{n*j&B%IGA26cBop90qD*vfsS*qgxR6yfl%cybgb&q}d{ z$P_(^GYL#QTlAP+4_Y}7q>P8ldcZR|leL-3e}a|8%;emwqv<6OS3|ZMe}4MMdk3!{ z0USyD(?wg+)x#GSaG%MxfrISiV@{)NT9lVA?8*h(3rW-~Yzv(W7qHkS>JEMrz=c=u z5}6CSXUAatV`LJ*B0CFT>?^jzdN@X0Wa%$kX3K*-x$N#R|M4}O+yo_L;ZymBg zIFrFHjA&cG;)I%V8r-e|C7^*Ejt9C=KUD?#xk68>Uje)SZ4)T_!l;i^xnWhPxM>tX zdOI$@UH*7&kws>Vb>zM$_Ff!cbJ}eG=}j?2%mx-?{`bU*KcESAMfHU%FcxTQqFAKQ zrtZ!c;}>b3|7Y2%n_1Ghm8%ze`$;g~>QOeEBHIy|Xp#tOl{gf|9}YTz9KK{hr}lux zC@~m2=|M*#lk^Gw<`+p%_!Xxq?77=e7MpyznrypI{e|kRfTs5HU*P3QG7}M;5?o(- zK!>b4<314HEzOB0DQvVnxC)1sJEW?Cq)_IYRtl7`bLD4T9HwG$0M?LMPNPqFnvfkZ zraNJd(ZZ;2G5AW49M%;5#w72vcC<;$KXNRzE);|3y1hC)h64!{2Q_OQ^R3lpbG?0i zT6g*RFaSsbkdy-&SMuxNvQ~8qdU~J|ES|l#!UH2qzup5e;6jCv%4AQ(hOPlmCn25b@RXz=#xL+k zU#Cs-E+;e=$^S^vna`j81<1O`E>0&^l2zoq8;-HhtR^40{6kTFvC~`_StSbDo+gKq z&iG4AvV?z4hdLs;3F*{m!s3(;nzZ3a58M|6tgd?8t8+4wLt)ha}j@ybG_#tMY1 z*w+ZK2<`A9s?INUp`tu4$DoiS`DN`cKw>Kx_yT`3p|8Zb`lETE3$)4oxzGQ8FJ{x0y3;_b`i-kjY&ZDv&v`Uq%BEJk5=Oa()W!dew{JQBrD0J z9|5f@ZPc&4_P~H%ebt*IdTF(lNf>7^I^(gfN4`0)v+YZ;v8IJGj#2fiABOy>u>AK{ z{g2-4%d?dYt|9XioOaLxhpfg^>Ek_Msqq>ZerQq={RrDr@FuO&pRiNJK?K&YeW+|Q zpU}XV&}kA823t2B>}%XJF)U-CKckOd)$CFb_lSs#gpkXg))6!&(w^)wE+OQ7i+K%K zpE?;mdSgE`)wvu5&Py<3RFDHI+fQ1xy;xFRX^j&sM9%g)_0vo?xb3roy4g|P)&!#& zsfMSPN&1T)V@a@iC0>+q$4I{#W-)ynzVXVgeV;z7*R%j83{z^clCL92Clxi-)OMQY zT`majP~k=zYUpVi?TM;q=b|ursFWJC85Q!TsCWhn^@=H`LO>o( zQ2q=jD^rM(*rnR68$}UZ16}9r|%v09IF#KH- z>=f>jK&uDpX{sr2nyrbIuP0SF&Ab8d1QLa`LZ&nGz{0VdJ}hejG}uS64+l6;)coE) zc*V)G+LgUm3s_+6Op~!RFUm?rsRB{ANr3U4PUXAazR5BIx7d%3(tVLTDRXI{0Cm0_ z$ICw1-#hN&*p~Gea;V%NxgS8Rd8G&23?$A`fz7XvZW`MY63+wud4#w@5H$$)G34Gi zc)}pf)3AhBtLqBZT=6urQlYz&J;V_bI1NYm6qNgH>v?O{vF!nw2%-=JJ-wY*rFr1! zZ)kpvlkdYidG4|K@5^NK7sdlo8ec*ikgP$=_FU?P@{vqp)IZCcmR^w-DS_inJ zA)!-eCQ^d)B9ypWM;&qBdQ_0gXcTgRC_b(x#$*W7BBr$w)N$+;~T-%!J{$H-{ zjcDifdkNwVr27#D31ZxEa~Cgt_qbi;a%cUyoC;vl8S)>dHold`asH*jutwgapC)gf8C5&g#e1!KbZ ziV%}{{SiimG{1tzvZtyRYKe^$hW^6bj<@ZOGc&BtftJy5lQHzqTZ1;VBKUK{b1TpB zmdDGdb|Ae+%QdN{qW+30Ewnllwd={8ea?yk_OKD@qJQ7Vg|UhllY@o`Z{G6^Z0vH~(a${%FSQu^2ZjD&LAQ>B z2biB7BELME-|}!xpwh3KART65<7nq_d(cm?{6MM46`a&J=At?YE%bw*lY@w!sVBdy zCT*i&G7KqIDX5v0(TLA68dtU4#M4n!W~Jv1$u2rs-`3tVM;LTnS8+c1LX4qoPp(1Q z3!0=4oKKvQkjBc3XGW1~z2@EdF7fu~338<}dBUzc31YS!?vpOW)3I*I@5b^3kaEKz zh#Q266VZX(zcoM%CFHF<2#si1&fV!S`@(Cg&M zh&-;YGmzX8muT(f=?Kz|^WLY%2a7V5t1X>H&{Z-@n)nOL5i8n)V(5q5-@Kx-VfV~v zAM`6qw;;E}ob`&%UdiE(;q*pMWKV2P>8YC?4FD}Zl%K%KJ+co3z%7S^M9Ed1#iFV6 zejoBQ9aQ{SzF8WM1;jU-F6Ui~UsrbpI_oU_EyyUn37JbNi4VG)OHEhFQ)?Gbj`{pq z5NWB+VvdMVl>+hoRIGNt>^Rb}ig<*brwJ3;`|$|2ju)O)#zT=+4Aaue`p#}{hsD`4 z@o4M%Ev8I^pwpQ8n4LQE2B`sr;%bb<(l$`}tva(_u=@zfyP`jgD}3>7j7TtT-vd$L z&FKZL@upfSoxTa35_46E2QG|q<=>Oug_vH;Qjs61HyK-Ywdgl@?wY*e?MBG`wukA+ z{Y#&xS)Zr-WHN6Tq4~NZ@m&|?ws8h%6P9Z~+_bV=XQRB>WIj(&dK;N#HVOG(@=Jv2(QY>*J8sHAYw|x*Zx$ zi(VeGDMz@!MWTAMIkiGP{kxTA`nO!I!?8@yp(zV<1d0YTuez+#H3qTAACc*x1!2 zRsvj-**F0lQ=WnYd=Ok3-?qqd5-su0>?!vbb(^ zLl>_+|NE$F(`usPES34rIei^{AME4xKjBs_2Q3$n=*~B7_g$Z#xqdhBxDK`Nf@$eq z*OyvGwR44!R4b_&>)r{Bhc!^{u{Hc<&N{W{9ekB{IYGL^?p8+SLa;5vTeMJIA}818 z*Z$K^Xi~26hofz9qs%;XY>T9?T|zywW(zkG z6#o-MNrFAD72Yg=pU>8E)h0d3Ff1V)KH?%j7}$r@arx(ah)$a44e0(c-syw25uf$D zoVyRWCOF`>Aw)_7AlO{->78;I_`_$KUqjw^!5SU1NQ1GK1;Z;`Nvp z6Ao9ZCX=evl=C$8W+(Ob4n`hKpXbS{z!R%V^p5ZyF@abad0Os}3qki_`EhJD#m=4A z?+@>nYTa*4zl=@(=#2w~`i&S>Yy>^{>+HMt-Sy)>7IOmZ_nz;qRV+i=1;XKdtay_- z6YnfT&K{J+kMs^g=?nYWZuI#rX@tas$xiWin5v5-SQ_SvZBP9^_Uoo!j+YY|zw=sB z*Zdp1;H}Pa*8Y<|y@meL(~0SKis1J-*C8(suxS?2k5dQ2D$n_|4qf-u>jk^L4hwh+ zf@GX9G2#zzC*wvV3KNRdgF9tC>ES|1M_4}Pfcwyq!XxEE%WhP1ZV)u~%hwMC5v~i@ zLrEZrX^=|vHAH4rR~QR8F*K<C;)x&LGGyU>Z0G%$(OkJ}YW%Krz*{HIk zMn!_~j|`^cQP&8is>CY(Z`ewliQc{nP@&d9*-gk=BdaXumAQU2MI>zSelm?Ntw>MU zazlnu@oUzF)?<+eLkMGnIl-INFd5dRB-Io%LXoNtZuN`Jm-5#4Xx4qZbYZ31P{S5f z9~XWt2tF?tmFDNwhcK=STDemO#5cy>T?)kqRg{6<4Gnz|Pom*O0qcLS zIise|g2tZcIbZ^5JHA}D_14o$09lRrhzy<1l#wtQR!nyT{&lU-mR4`RZMZbWkd2<$*Lba#92J$79x`0 z_H++&W`IRJoJUuUPd&HF*S)bBPj-H^t+&z+wWVUMwBAg$ z9s(Q7!Q$hrK&M8Os||T86>=dQ2*|B)K>cW*2IO1Xct?oyS783sOGXIs-Rd)zb0$mW z#KDtzQNPnZb#ja$@gmt)7?_!|E-)O@=dpF>6nE4~n?9p6Rcq``um!UaB!6K&ZS_3a zQhz3evV;1E#-DVmT-vO#k_WW6uRTD_xvucbR*FcM`j|WQibF~5O8QDFh{Z5Q8DL@5lanb6Ef6FqB`XCnaj}@aH z1k^`k<{TZ+uO`GpcwO9kt9gW0IX}0`h&cZqApkQ*T{xypX=&+ODS2`<+i^bRXK@$u z8tP+ypW|{$8_Vx3s$Ec)XbJ59C-=}xv2mc-$($YZ+5qVDHWU0#Yw7Z<|vJreI6V4b{T@Lo=Mg$>oy>g z?|mP33%xAATOK^UQuOegkW+T;EL3!5};^tau5l5;9X`Fb4`>U(4Wt`zNf_aJ=0o5wT42kmsty zL8~C0aq>qr;$Jkm^Jg(c-eyGWFB6(7e;WA(o1?@VW}(w43F|XVqp{cngy3uN0edCI*Kew0_$Qb-*N2wWteV04cNYnMX4!rM@7@q=X091fmpN$x>VdT zQxEQvf#%{6chT=+^3bfeG4PcQgn~*{xhPKLsk??T669D=x#z=d_FT>OgaQcWCwRfH z8SivWPu{+VORts`qZ6Uz;7WeR;Y;p+&b)8M98s(l?SjdIwfY6;3rFViKXei7O=14_ z_{NKkaoWNa7MIcrnlY9rE4N@%zapjE;sQ@whwndX)f_moMZQM@+U!iHNQ-2q82vV* z;Qo!J-*xyj5#+E9@sp5msC?T@NRfu^QN5D9n1V8G38f|B+2Jwh$yUr9rCOA|e=?NK zMbi4ZmT!AlV*>ws%?vf0422GD(!`IpJV3fs^$YHF0+A%xOyKjZspo6T^to(2L2Xuf zUD$HE0rK41PvWIw10puBMG9iO8=b&6XTrq^)gO;<#h;G{?nXtbNd)nD+Moq(Y?(}R zLL<6!cWHLq#Aw#)b&pfP#jxRNyPNCU)hEvCOe->j)ZXZ;2RRk}DyxdF7Hvs$)84MW zzjT|m?l{Z~?zu&5sbV#G%i-itCJ(99L|+kSaq(75mFcKTnKOSsP2a&T1#XyP(-E@G zSD{~%^x;jH@`rT-ru^bb)t!fSWc3WQZs6Bqz=+1J>P>8Nj`z8WHTSGm6uhx$m#>ce z3)Lfx?NKk5>Gghp_6sFI!$?hmQ3O|d5;rvCkgKqtT0Qwi?RjK`pCKWxw(4o__fXT1?Y_!jy*DYLy~u$!oNVA!@cl3R z26E=H&DLN4lK<;ZemQnY;kmyRZZ6ayz?6yfk!d~%P)Jl>zb-x~mnE@ev$Mw@oUUJPk4&$o?l)*a$;yZA-0bEdCMzFH1ixDZM{AZ)s|;orTy ze2^I2ieLe)x@CCMAY9r#d-5ww@@DH9CbO?EOw55L^o(Wp&5E9>e%daCobx|99x_|| zXwuG`k{SxZ4oJerju3yfq*2nggqgpVX-6l$ueDSimm{)!=DbmfWxto>AtS{3agR(F za-T80iR89iU@uyG{i2)c;|i;iAQ0kNjQ)d`3=Sa`iGD)%3Y`lUm-hBarOAxB^nYI_JE1D%Ha8uD7?+P$ zs?4WJUAygic##)ydVP=Xic$wkAc`1u`9getZo+Q-%(K|`$k?uG-yzP1?(KA*$=%z1 zsd$}st{wkcq}f+`Ce|VZ-}K6HgFEKgZsz3+&yjaxFLFe2v-o}Jhc(XPBB4QLG}b${ zoHw-FZBHYdpu&#@1L;DXR=2g@jK@( z@f2vag@*H9bbmMli(BYMb_=mym>p+14nYhTu^jz}VzzdF@o7QzwV(#ddBPdp%X(x52g@G8y)=n~8$xcgluo zFnmd1)pMvL@5Z{*{FwGU>?b4tX~`cpna?3ttkK2;JBccfcY99dXOWZiq9~Fd&s@5E z2fZ#3PbvAG5y%=OK{%mvgh`@QQnN%Y?nTV*RVH zlM{olHUH7;H_1`F$Pj=nmeem@q~QmnsxAn{TfiPB%UF7Ga8diVNccU7M8el)D8K)v zPShGT+~5u=i_xgD?i`qWO$|<8r87XX0SRuiPi1%JcEX4p zxt`klz4Q~~%}6Lk zHt+!VRCK1giE-#C#iRenB=B$hUefV|G9~nNKp=Ne(e?PA-7L9S#;R?WISP7O`+o`I z^=74d%BSb*m0K$ZAF5RAsQO^7RK|J#GrFLK`HpJH z{`6`<%JP8l_@hrs(cMfl6&xCF`I&qlI{o_dt12+x5P<-LJl$-mj(uy6%?WNgw2Mb& z+7nqEJN*je^s)6$l#UoLw=!$INsrByv?wZV9e6~{d~%qPz;GwmyHw-H^3k@GWcGbX zWK#th=zpi4iK}lpuC&i8Ua)+S7fGK6g80_WWBWgcl^qzP!8qgXr+vA3Km?ZZs*=wj zTMk_11k4014Y%r}y6wv3>1){gG!3!cI|_FjGEz0joetN5=6a40Kgjti)jsqhXl$vE zLje3iLH|1`ym0Q_pfw+QnvfBqZR-csm?6KYw`s8z;p05N=&dx{36?ir;ckGNLfQ)i zq1Ovr{{vn9B^npNY<~9dwSJDM*iVQJ?+B-dzLysbXIu}b4)Nb@D7~J)g2lhEhPRZy ze$ZvpAlKt-IfM4vlpc-f-pb`C|7S{X3X*3$@r&c4WjXu(0}-b$pP2_ABxYBi_sUFT zJUUn4ggMn5D5f`AYbW;kuxHoF-Z`hu|9Tfc?oLqTD4 zn+RV!|G-X}*~$WNf_&qR`1$*m5(=@_wA>V5BLlyc6}wk*KO*1a?|v&Wgg)koX62#M z!PJ-hZAk|@2eqpTnNoF@?)qnb;1ydMM)?G-4;c4(g9}g+goQF1tF;dselbVh52_ z02%gOXZ6x;;Yx*o16^JTkmLPvSkiN;FDI>Z>~uFLFLoQr&%fydXtI;WO( zFWL|~vM6?bJlR{!yJ0Y=u@QCC(?CRsH#*ZbRXq-1guGpia&=OT9syUCqXRr zU7rs%?vQ3Y1ADAuGCeqhPk&qY?le-u<{tHsgem9wD~yvjCrreLy}frYdjJ2y`%7Q9 z{Oo;v1q$RcbVb$FKb!B=H;CUpb@sjKF>m@46LlcmcO*l*YGsnBSPBWetz|dF&V5oD zh=&>OKN-QY5bdVmQ>&gvGT941Nle|hKpy zIVpN*yGEz{xto02VE^4_Zh?`A_XjUHepl;yAQHH*JY`n|&O_PIfy|w(w$3(#1czMcZW!*bf4`8ZTewE4hR43kg*+C_6}dYKh?ysCkb99}nnH-6f7q}#lk9dw_GU{Nl799j}16?IDx z)@ed9l$N;EX3R7p@^w<-wmt21K5c)*1O;otrEq^8QT~{nK-S0rZ*tM7Y5Rt2vj5fl zGI!6K(u=Z|%N@IGL940#TJAMr{=zBWz2#3WM<{~8?+LZ9h2Qloth)od>R<9Tg>{V8 z{JDE}<^rFW#g8NJ_{i63h1If+pXp~KQq+~1&KKab_td=9wpCBlfdHo?=PsJMn4v8+ zKBVorlzx(fGoPMI5D!>vjf(zeN-3~4sd$w&wBs%(C0ZFf!C==qdTOL|hJKZK?)hk* zMrtapDxt>bHTu?{wK5Uj=PUKUJDfBr_?(!&WIpy}K|aW_|NE8Dzy7N4za#hcE z(c&*%M`S-A+g8KJ7*5W*y^N^`OxA@QCZ;~v*MPp78Rhi+T1{9GNK z|3&84hATNZ#2#?I+A3*w+nM-wvn0E1y7^&JFk{~kJEt1{3%cw;lHkq*FXX+bntygV zrk8UDCDO*dajOxC^lsp7+_XM(5ZKHa+NGcqd*JePVYxamMNg{KxMrp>k9TNE5o7zV za$S>*?@}xGDV6_Az1WC)F703v46}S<+k&dAOqz*h%Zyo zQ{KDnW(b|sM?S!V4-5?KF zlRn+cwJX8+CzoMqCB88N`Vdi_iv7T6Mw3iOC$5%$ZLzrc$k8oe4~t6X;HXMLAttQW zub?;)32bJ~>nJ$EA>qnT2tSB#1LLQ9r_IWIDJaHZ{wlL zk5!`4Urc%M>Y351!;`3W+c&K6@1m`zG6GR=Q__}LH?!kb{Rm6NOyp6AP+o(#KhOKB zQXa1KrhK~oa9v{MpO^1s>SR?n>f=TpKd7OH(?Al~g=zQde8gDL7xUsU@`>*?uhbnEv8k-DV!%J2EKgQbJr^>GpOa95$T z48^-+H#1pj_EK_C15%_Hn_bgASCuZ^*r&_-T1n3t4a%Soeou^9f#25?a-|O)&MXo| z%^j$sjZCRjUbX)L;ccspy*7#l#ICd5wDoj(&`9+MEXqlK9{=aAEZC{TS$;FG_Tn%4kM?T>URvoF=APoqMPIr`+X6m2zll=9-9uQ2D|dYf7EGH*K0} z)<5q_llzbs_UJyKlvNN^`C#`tOQ?^xB=l*vyiso|@7qmT+gYImYXUV^PVL}OS?9+c z3)bJLJbe+*P#^B4vFp)xp`t;bWlGvHsakx|eka3xu*l?7Id*T!VP>Fq z9y<4uqb<=ifQKrd_tn=}vZi>#)d$JWxr%mQwDNS=-`P{cx2E{Y4L7ARU;2x9H~55Y zz2e?#(|dskxv!ozBz}Me<|Rta9GQRxMyHlFfiB(A6Fu=qhdnFansYj*&2yr0kQ|7W zmqCxRwV;sP28)*JGrfKJEP5FNqxTj?>iJq|+|<8(aao~=2|UW9uuG-;X&84yrU9e2 zQbTv*7p?Y($Ef;P{F$;psbA4n7n@MOmtmS^ry}?Ti~iXCBKNL#umw_52ceZ3T9>w1 z4%dV6UGI8wz_r0S!yJg8I7^$%EiBP~C!V77Aw7FQ*h@aJVXTSFSK)D%rM9Iy19&6p zm?pzZylG#2TYU>7; z0i|a(lC5dkY9vw5)h`H1w^%}Y=dg)*$?FNeFCB{)iT#udL@3&NB0uGE z#n^BI|MfkV#E$ooCd{62>_G#MqX#{wU5oWYGtI5thj|uvzK)|NA3F`nzeK;Tkt^(d z(ed_6DJKVE^e3W4kpxN~eNlQ-p3pLxc75Jnau!@sj2y@{*P=0gdMHiK!1M0eF@1Gm z+xV|j)qz{o{4Vav+HASB^1Vf#UsSgx2p9=D+PtX5p_<-8pM7h>w|>(NkB}$#Z)nh? z_^|b^r22n-^2e_%RC^Z`c>D?oNbq05wqvtKcX;nY|glSzRd_icggZ!e~P z0iS&0l{~PuxbgEH$L(oVd1Ubn;ab*SuPGi>pimwUX zcdu)>{>PJMm5uW0Mzh#q>&@kK>QY+PxkxfSKT=?MdvGZiMaO=k3R7R!k~JYdmvS^+ z@7KpQ(15A$_Xt*Tkc0X1)@rpoaa4DZ|1eQ;zJWE_BB1FI7oOgKGf9iTAo6Q9?wMBg zR#ivw5HOb_liBcD!Jhk(ItBRr#Eqw040~7Ci+hJ!?WXE z-bD7FqB7_GZ2`Q(I1@Hqs01~b`j$pjKxsa5KBAv3KHu8AF0$#=USQ(4-hkZ_?CQr^ zAQL@~$M&|Squ_HqyBREQLYqJM>8?-p;%Anu`y==_pTlMtcDpl8U75~ZUXSE_^h!#@ z>_^yrlnCSK_cBZHC*031hQ5_9{P<6$hl0f3)3#p0cW=b?wN+5H$-}r#_h;HkT3@GL zV2rYYP1m<3PSr1N-t)(4*k%*{TT`?{zX~nyA8zJ2RF=@<(3HNg?P~m|k4x^93>VyT zMH2jN1#N$Cwj6}Y_jRlp;X6Dkl)F46v}Agm8^H?VfgXSVy`6fLvX&=i4s_JM?nE(% zU!nEQPOua6VCO1M+uVQ))Awgb-|(rBxCpyhl04!9Qh*9=oE4&(XpUNn`eAh=3a*f~ zDfn#xCy3u7@jG41XyU37dgGAZIAFRSd#yF5Ujq(ZSI_cK*6Y8EEa&Ff_Z+FW@Y`K^ zNm>S}k+1L^+jI%LK&1$i;gQd9JSc`Kd{{hJ>MQ<)!}Mel*|SE95lwyY{w>*n%WIgK zlm{io@;N5Pw`>Co`VlHzRAp zwQzrR!b(NVo<>8=(IgVF`KK7lV}!75F($o!j$UFyyOVX3YsW*?iLtg8w#P(&*-R)! zW}5OnQFcRSn;9z*k4r#J-Kx@n`Md9Xv^2=Obb{U-QnN zgL1RtrvvYohMQ2Wx{2xA@G$oq;;rN9>cLOnwe4m*$Cd-%>|o9V%h9Qj>=1N`Q{*#m z`VqcpY}@NYbvGUdCP|y9!^FT7y-x za)ao6Tqq#NMbh!ukC$Lt-sL@3S*te>g+EKL@asAo;;d#WKNdhtzb6ZxDBuNHAJ)@} zIy?q0sy-i7b$?7p=v&zJh5n2uRa$qR1XtAlty2`Y2Y?T(tM-2XXqE zOWAk+Ua0&}A!>@Mr;wN5#a`8&ZOv>gak0KLDCnbl_>20TSoE8(8t-^SS4Y0A0d1T6 ziM^o3s-?=Wrz$Zp<y|%XuMeZT5xDoCB}k;TzhECKfz*UCV=AL;Ttx5 zDDcStU336hiqwJ+%TGsX$C5MAWN!61j{cE%?E?f9W$UZ4yQl z%?5U;x~IeUn#i-*txvEK^!X&uutB+l}a>V?-i!s z#rP6JlTWPjUVu>NFa#wy_~pPT-P}+C_Z@YMQwaXR6e}B;Tqz6G6Ldr|l&oCqo#Ml| z5)RDbFV%}yzVo&!&9pNe~5A$Iuyzh#vMGap38OGLbC(rmEJE|^Z z*PgGB%f?~S)%zHsOSE&_zk`X8L(#ym4^IM8;&xRTReKw7MW_QfHxxR)g70ZF7bVy+ z4wHMrIX^Z2?9(nEwN2Jg_$@)l)ulnO`s2oTz?j8WWgdHfoH!O*hI7syUuZvI#hU;d zqKpu7&1Ew7((BkA3#tHOxyw_-Wt|KyzV`Sh^Q-sL2<`5# z@zUm^T3Hb`TAnUP?EckLVT_`s_s6A7%R-bM65l#h z83;3`)@*HBfLLtE>PfLcp6m^RaYm>oJN%i3>R`_UrPpacYUGdgO27usXQ?!KD<#Wm z7t_&hzC916Q3W?&XqD+oIvXIvSN0%3cqEZ?pv8HiCpd3qGyiqrtrV%}zWp2SfE-jw z-Yh*(aaxhq{nCG$czLO8SPEB!e#H7e3*rJC>**!chnWUrqjZ*yQf$WPC6hNGu*Wvi z2gNU=l&qyHAk%^tqV`eT0Tfgq8jFL&Q zH+g@};1PUrYY~ikuKNh^m31@Rm?d*Agmxt*gUU5}e_730MmF@nJJVrR>wYw__+e5N zFvcPo9%B`iG1Nr89xFl38m)SFRkoG|&^DE5Zz7eA@g!J2PJ);f|My+QZsA?Fc;#4D z&9uU`L*(Su1z;I|O!DX7yLLrl*NEG902!I09yE?$HG2i!cem57!f0$wqUW=df4>_K z^)H_bcN^q2Nrr3BS`HNbWymMy&}vSodHx;4K~aFghcc<2_>gIu!4t-2n+(TBu_L>+ zbK;~Dxw$!e50t>(rIuJOr?crUQ-TyJ8A&p{^CM|JLp5QLdrfXEzqTSbKkVn%PiA%| zoCuc&>O-b_2LEN=`>eeN?+1AYV1tz)AVSawu_C@FS3dvLV#_IrsY~Xm1(gpATQ-MD zwAVC&C}g)Aj>r21_e9IZUqDN-zw)E;OtY0Z59tpE8&G@Equ!wY>J2vnPYTsT!egxg-^A;vB~om)4Jhr&|2 z30~rM4hc=J%ihjY7Kb*%fDS#VAnUZ(xWjZ5h@OMVMNj!KQ`7{(QBmZMtz$;8I^_$1~ zxX%&7ftm4-J_}$rO1CL}<-Ni8+LmSZVaYqysG`hY5kX>8ov1VRcoNV0GtP9o7ues5vI5HBU}71>U)@i@(dRZec!ChK8JxpYlVS9rv*j%n;(Az z6tbGS)|DMb*D`PT|2Q%#Y1#dbm>ky2CsIzO*Gyx>&w)G%79;ESwESLodh}0?v2$wU z?*uH)iFry)L5%a|>X)c&4sEh`_t8(R)y6~K$84iABZWQ7st)?z+J3LTYrKtemZ zND$qef%i5PevVHwRk_Ak)!Z4YvIhGa+255Wn3onTZyUJYL}3FmpU%g9<`P$7?D>4W zvnN?2nC9iro=3Sgx9GKH4R8Kdi=4dv4Tj#40a#gY8ZCMMGuSiXxcg7%|5I`MQch+_ z4Vz=Ac=a_Q+Lyz*yJ_MpTbxqVA(-;TEtikPr%T09OvCWfK;G^@fr{h34jrkX7Fhvx zOCynEOl2<-LKl9gTyQcFh4HDc?fD?$fY*o5eCxHFyU}vTK)KOk{He*og z+k;u;JPY{ zS`N7Kswe_-@CSpe$t#5WSW>ZnGiJWp{o8#)U^g&y6ft+wPAL03sq;Ji;mmddF9xeX z8i%%iv1OZiOAbdHzA9$I?<|MS^V7O0!#){bi^ENG$_s9>Rk5mIn(oW51W9+3;i{}- zD0$nmC5<5=nI=Ir@PXH#kl3Y&z5|hn#EYF?5dY>dQ_k*g62FO(c}|60rjX8!w@;rA zd5Chfeol$WO_2SXEz+TtbCT@{{W)NY;hUJukY=$} zE0ZXFu3iJm{AWY~0c5A`+z?tFBZkRU4pbiVX$)_aiqgn)ndOcVSo5dPpT89|IE@Ik z-X@Xpmf}GaHme`yq+$07YgUxsE%;}zvALfJco9EAde7z>PB&=tiv;5ZZr+iokY*v2h2Q3+RSjkDRhh)?Dhg2KA>MjcywoXT zv(T*}G-h_dP>Q9}Ov^KB>iMc35-7iDjB#K4rR7gJ_26;NtC8aE4*LxLCZbc%aYZ~t zn*C-Jx0pN%c5}21e0PO~E_i0L3XfJiAR+oWlB!yBKpfXI=mKHa`6>iYyd95B7Bro@ z5=@7j@W1u$)!mUfgWd(7gSkDE;o(B1hZ`Z^+2h&o?{r=2SVjZ8W~-Q7NLqdlcO)Ag z0_I3bzVnh$4p<USh^zILyupcVYiVXiNyl zcrvuVI~&4}QnO}sT-mrYAY(7|(uRQbeqoKkD!;uF9xL!)-WQT8;dx>L=8`H!5rB33 zhE&Ns7!dx3bP)o^2L93ji-^birXE8x?e-mPKsNX*pa2;E|9JH%f(m>JTERoG9Z){{ zpMP09qBLx7o4$RtRVk>rZQ*~uV_h|9ub}MS0uVe;=r+;NOz--y=I) z_-(JZk5ObvZMSsYKK}okzdw%I(F=n3(i^1Uo6C_z+yhV}0u79qU{h(3>44(F*3NdwUJC*0{rny;tfX?I=qWZd(Wm5f=O9Jb1kW751X#?gpu?>VFi4 zyH!Y-B?D=?;DpO1&;Tng}ogH12KmQ>!ruahih%Pg58bg?@DfX1n-AE-Pz?i7i|9a|* zZrf6SPkbp*ln6`cRV{p^U`ZHmvTBB{19^&h&0y$pj>6Q*J@c_9aa{Nm&tU*K%BkMi z24;np-$jS)3>W3AbC^k3;f_AZ6pHgID2CC6pGk^CU*o%VZv7=h$e+lgwq$kHsXl@^ zS-g8YKT!di5bcnbZqd(w+r3qO_?OPp(n z*H$FA46vBH4g;HK5_?YvMoj_rwmtu~>d6`te1ky@{efH+npB}rqUE2&SipIXoV<`1 zDsKT&<7xSy>`7$3z#Ot`jCi%mGvncA6 z%>%ER<3JkqWS}8hX$tiV^#U>3&5F(uQqQ>+#oGo|-WnjiNORgnJtYY*Sy%B$Sp^F2 zoU(tU@Vb(TY{*_-r)7P^t^eOQT^^vY&`&=ycy;{=Lvi+0QrOxjBRAI-2Yh~o{gp8b zqs#<6629#`YVl#bN%JY+0MQKsEux^Bs`xhVal9m7HKW(>D||1;GtNXP)*4g`H_tK_$NOELo5ZDunNM84c>KNStH{S{-LZvFp0Elx zL%+28hfwAl!W!F5%qxaHw^YSDK(y&^duD6CCiEkz0K26{_z~-H=7uTQv%AsLyN~{T z?8K@&a=Ou-5%7$AWvSuAl~vZlL;PeORblKfA_Im`!3M;A!qtV-B|HiufthI(tm-6K zNB`bT)wS{dHdNURc>%`ihS^MXX<<(W`jZg+uRl31%p2ml-~p{{LsA9aQsj)Ah)uGt zaB0Q%V;*;2u4^!XqZFHLrm~D|pke&`N&^*7?K(+p6&HkkA z&$A6dKf{%q3GNO_z*Ns8)B;NI>wl86&Jpqr@;dBqvPt8i;aV;nq-Z-1cp``GTcl!M zbQ%4bc4Q|Q{9OH|4hj+%!O!3!hI{lz`vu6oywh_`Qu2%chix^nUq4D61BP)8IVDSs z5fT6*>vAiy9aEJE3xquJrJR9AbC2ENbAU^u1t%B1hfjLbACmgP+j<*!lsr|{@&|%)Gq14 zGIYNFgTh62$-gF$2{%#V10tLHpTRl%M(KyUr@t)I>vbT*&G%R*2Q(TV<`A;0Osc^t zY2HloF+;A~@t7^kt;KQSXE5{?q>3)iqS0$99aEn9be9;djUoy0A?suZt48})>(feN zU*haZqvC*f;%bAzjT9Yp{HYWftrcVb^Zi}AZZ+xfp8kv_ZO5Np$d-Y^6+hJcHt_9E zr{Z}#O$HRjkoWsvi|=h(K^8i;22(%L0XwPmA7?u=fXIl5NQ!_^o**7rR|8gy!k$&( z4jBvmE6YJaq9BUa_aw$4-3;E);V@Je94H+a`msQIfcWJV3KzwM{*FpVyaI)nl?YRA zRVBjv8CDQUJ-voHOj{98xwxdxxMBe0Dyq_E%BFu{&;i|61#Rl;2a7F)c z;Wc3`1f66lCQ+OroJ&_1xT*G6hfJ3DwL{LnhJ-thgIm<%MaYz*ypl!PK-qaS zA{m?`%-<+xcGZ;%i89+?4>il`5t@xUz4N`aqca0slK=|%2i&Aka^HFypTGRqv`*`Q zy(40&%?{~#Yw~&QyA1I3`zRmW3BVI!&}VX&2zTAnNL1xB=el31uN8lXhw`O0j+c^Q zvg~ZYvD-;VvdkBI?om`NIFN6V!@p$T6B`KQZrBJX<6Fwi_N~wvX-#noig%IHhX~qsC6QL!gZr=EgP|qg zdMA05qL{n5zSi_!O?XYxeZkR+w87il0zc$hN3|{7%zEJtAp{VOB6PCrpz9Np=Y#`7 zS6mfDB0slKvC_KwIKbZ6sipz*8~y7Sa&f;MobJR1kkEgc0=Uy;9ZkDz+ocszK+?Eg zh5uOwjIlIV^n|W?8a0V~J=+d*curEZR02!@qb7w?lx-5$HK}pf^x6@Yfzj%3v_9)$ z`uCEi(N0YbeUp+l40D>YCPeFz$*N?{fQFSQ&1v93eF0)=oY}UEAFO!%+eycthM==h z2$rzvO>c(+GO9`;pouFF`mg|0>Qrv@O97T26Wn;G!Xw=Ec&N`co8+-=*H=Oh43T*L zC{Z@BURZB6U-+sriU4`IEDqhp=2D^8zk?T0T=J--vCqLaMOEPLFIArXK!9RiO?IVl zcKMn5%ur})%5k9n7`_*;!A`Ce6?3a4*mk&}H`q1St6J9##^ENWTwMyJ3O*k?K~&c0 zm$74!K`N{?_eplUtQq3=#AZj=HNa7E&`#h?uX8! z>AJ1YlxqcI62v;$jv_A6x-7aM77K7r?^@q=yu1={l%ZCN(r3S&i5xCS4z$kpsbdiS z9F>ED?Wr3sb@_9LxB2xSh29uR>SS#r7ZTt0*EbS~FuCg3)#yQqZbh40?pCDKHmK2- zAE6TXmy$w)eZd_);Or$)L#ta4A!=joSnu@n#d5${TLs=JADY8)u9>7>xG(q(X{RDk zza5>Y92E>>ert+o;*V0JQ7V=k$c36mike2*Qdh>p3+U?^jl<}M zUf6ZL0AeV9D~8IIq^F$2F(z97PEb1bggm^a?aLX&X-!{jk!={fU#TeWnqY+tm{M~S zh?L#PI=HvY+~3cG>t;}9qB?BVh-C)q!1vuQ3m2ne$0Ciw2dRD zJgJKcn{aM~=Mq0$dP74c7hjpX{DIN0p665K`(!$iCL}59JCH6_`Xo{|gM(DfsL?7y zLzy624K;@7D2mct-}mlmty;P)0-+NCNqE2;o+Z?8^G=jTfa7=*&g+D$}Q= zUxQplU}n|8JV&=PgwTY*D{xOOtY4;L0yMOWC~>tH4wL<3Gey=IkCl*E{ZlMTW^fNP<& zb-5)U#mv^7fk);Fgqe)l1uX@3T(eaas2gZsK7nQ`#6j`#C$;|EW7{{kU>KUwNt(=j z*Ec;0Z&5S|e|@`y<$u7>VzpI`2xQF@BRZw6U>@s1>!W{y<;kguo7nG?;X=-y2k&uU znH98%=qzre@QQszogyzE(fdg$3J++Ij;ZfNr_`j7i{RJLA~m{1(!S8+w8RJa+MY?N zq2`aqCQI)q^M@DG@);uhy_vbng9xh6)$tHrfM2#p4XC?9-678CAhc|c$q}1cYAC>~ zg}j&L7D#qbeDb0rK*kn`@yts<-7S&^-FQw{LrwU^L^_SU1jIv9C&y zvfB1mp_eO31=-&f)=Q+7uKy)=d3L9Ac}JUD*{cX2QDWhyl()R;U9dm~ z%)=ZWC{aoQL8NWOM+;a(xyZI5^CU)Uv?-+ig<_y=i8^BhH%njWwcKFR6=p^(OoR>J zfRig{w{7`^2(_8C)cl5)n(jJW;vNAqL{hpR+8DA6iODC_iW>3tDA#-Z9qvS_oKNXp z<3hYeRjgv7auY*gB45s*5{nr0(o#ox|N~ouiS#pNlPdM7}5nSl%X+acTCEsCsDe6Y3#Q zqAKI(jbfmrk0*t`I@@C3d$T*=?_C`Ey8OlB)9Um=DI+h!#nIIopG;UWc;Kpk8!773 zAD%>b=L`frS`$)T;3MxCb`^$lSMa$N!yY(6RbZyB z;3~l7r#e)IrgSS!$mXP!wJH%>h^+=KD-K-sS`J)AZ`snQf;f$S56qf#4=)i~h2)Yk zOma0_4w)cq5ZUm!ewwQfWDZnNIgqI6PA^R0)H11fojw#n^ISht_&DP1dSd31f%Rt_ zq{wdPaTW3+{Z&YK54D&+dKp3;Uc}_bzNe>A&4|z`pQC-^r4#DT2R6I7ynOt;Unl{X zvX3`>n-`cAo1WpPr0>oye^}59e){`2^!hq+hgjE2PXnz?`UJx4eY(-YlWM~35RyLT zX1STvuXHFAiAtAVGq5qKaQHBk8yGY~sT0@pc2Hv=AKrMoVzLa`pt(NFZN_M1jf zz3YL|Wb0>&wLXY;2Ag=gzPHbt_cHXu^W@2YC+*J%2a6m!PjI}1%1Sq7K&~b=+^5n4 zFU)y~JYU@ey@qZOGijU=`$9SyN*Ifc@s@*QjQZU#>ZQ9G`=IC)Sp+>}_Un~akF3Yj z@%|DIO^Apn?$Z&B^DKpwuJ7QvNRo}dlqvY8KDI!!xH7Pi!fvi*z`s{(7j=RJ zZ)fodEKKgg&t(sdCZ$$aJ+12s0I22^K*I7dKWwvR1bNJzQe~DAMEYKM*EoY@n9rq; z9amK;sQ~y)8dkC232Sh}q>`i$SdcOLQt}8?i=kU^9usO64zDWLdxczH>GH*Y#o(ihI|+>DVmiBn{pGJ({B+bukaG zj6vskCPE2Mj)H9!g`Xm-7H-aaP-OM6&8mzc2hG8d1#MBVk(+7O(r`0sprUi@Pge#` zdfKB&6CJmz42kKY#-G9@@9-~kBbOlbGp0)M%)}Bw+y$ScJLruY`LrJ0+ap3tqr65U z3>ul5E@bs1N%;(nvz74s5$}LvZDOeB1qYC<`_D6tGCE_YiI=kAUtCKk2NWn7`f&;= z!Rcn?F%H1$EUhdW+T+NjJ_cTQ#>XnHA5~iqD6>@zJhvX8&evq0XOPameWvc*6#BJlQ@zl>wej7nbtqdiRF_O}5<>T!m7il_cY2M?`7Fr)yp*6>#jJ)YPjIRMf z_woA;HEbj|Rxxt0o?>UR#j8>#vIzCh6b<504YQq&;Xh5*A6Piqn51sC6V4)D zSHh zj(abqAJBNh^Dtc8pC%R!J$JE0e>Ae$O^5MmWpD#~JbYbn0 zTdFW0=JD>{bVH|lXAOfVslvlr+tz5T+J998FEuDX<^pV87mZAld5G^JRemASDeK;y zk9R?EF#CUdwoDbRAU}N#y%FqC^{@TtjjW#H#H-yRZ_o%KF`HZiRkTz>8b)HHCLA+X zYD|d7oTR7X2Simhd3qddX4Mk%aYLsDJaTyOJ~lq8v~JLL4-?zUmwRJlGW;BFzhs(V zu7&a{@llnnLDb-A$Z89agZrmJhhD_eC2OJg-keQ+M3Z@N#f-t6eC$T z8@K{FQ_1cxr$8OQYL|0`+xqsXo8L(OmAy=6m7gT2>^5MsJS@@KRhLv)l@H?VG%Ly% zO1@8z?_8czQ`+D?RjRbk=wGHgM&|IF;8QxvOnqm!?siNNic%jFBy&B%wCaF4jWT+| zD>?qR`%f7}O+;e>PHF&EVk_b?d;)gIK=&22_S1r!1Dgav&WL225Ys(Sco#Jp`|LSV zuKT_67qREEu_&~`5-_G9-}ThJ$W`+-(l8K@(7qm0Bg&M zuwpwthTMemh`&&SX}f9I_yadfk2Re01;#Gsyk^1oDis7l08(h$5p0X?DORSc7<-P} z&)2-(OOP*i73+DCA{Cv?^TBOE;{PbzDZ^jaUm1IllfLv=e$bQqL$37h0N110c!>&v zVR|?9%1eWE&ysR;=49G3Y@_hfelnd7ZJ{f>Nwb!)Qmmu%=|{p!0l zUnXovjbge!i~kkYPE#j-fDd~-vcT}%S`U>3Wd$Ydv@#0gJq5XO-#z99P_?&JNqNE$ zko2lmt=p~=1K^#SUd#v>e!VLiisnS$MXJUBAnW9?Rd9ss>9dKX^#|)gC!w_?5o}uF z6Gi?V(R@0Q>XO1NFUtr(D!1FhGEj-%Pwu-`-v}hV$&|_}a-m)VyUFC$_hSYcHeuNL z>ER%=7bK^HbK3pqt0zl)*4FhP&}CA?hQknqt(9>y732EcyRH`vS1V!kqdaxBwg9ji zxr|Dg4+F+&5%(}&Kw~g_mu+>3#8-?x8??72ruFD(vT+Tw+zlL}bzPTcc?@_&pfsnB z;?*mD7=BaY90t+*6z)ahv#F0WMTlc^hhgu+cm@*(>}UJ7xOhb5ctn6c))U zI`jf5`3)mX&>-wX!(*!t)2XCJC7UN#=7yfKtl)Ysldg`V&_S&OVyBAQ$D0NkYKEj#9Ki$9VHHgE0z$b(9YUNll5 z7}0Sp#rH7g5myC#_Z@rm8`@kB528IRQjDzN1!y+r@*Op@+~Q$7Ui78L0G zENomqT3Dv|ryp~zk`m{^y?NVK{B&KxmwpYWH0e(Cv5#HxWClTZcjK`tT3;0^jOCh$ zn@)l{SI>ZhtdVY3(69UBGki?ar@~MAUrXfTyJ-WWTpY$s*$zdFhS;JOy|H=v(UC8I zw@0yZ<1gJtDwi)nHG%eV6XG-&Kc$&gYmE9I-+$V4(`HRUz_Wo|+| zf)cp{i#}s&8Tg>=-&gEEeCig?!(lCmWkIaI%LE=;+@M|aPM6#vIpWCO_5vPpk+qps ziBz`6=IA85b`ZPF4ir^?{p^pdGdcDWKa`nu?=EUGB%T{e&i+{GbLn~$@e~nEC0dJ2 z1Qd6ao6U8}q)O3yW;^T8p@gjrwc7M%Iq)1E)3heGgE#`p2UJX87?z~=>y`C&TO zGLjjQR)#$Rz^dg6HPxDkA;$UQ)UFNdDu(apA#^lW-QYU;znYJ%ZGex)snTNS^ zE14V9D!6~h^NbGL?fqb%`y`XMA8hiFuIeKazhKz;g+e?MHh7>~+>wcJc1({kiW%cK z>W9L^nmgtMQUAcF~63%fVMhG(xpTIsn8)0M$&}tN!Crrf7#iWP4PwCTOiG%KstvHJIPhtiA{03Ee z-?*KL-WKGQ%q8zDbi@%{B8nPTm?4EemX8QEZRm&?P6N>rqp9kYn5BcwGWS^TC3kjI zE!%s0l8JsCpvyf-{4BveKr1NtDeAN6o~kuT1$^d#Ru*NN-7~p8;l1*nRXbJ{9Ukm= zVh0};2(4(J^zl*ttw|1SR8@#fgc}V0lZ_R0`(G9{1s1i)N5G;Jh(adL{>6w`iy zuy_b1_9B}}TE-vpPDNLH8Pf1&e&lrjLewq7xBYs`7@Urm+<P7Hu^s!gJ^ zdG$rlFC3zk%wZ-<76Hp*RM;Eh*3uq6;~}XK9=N(5bWg(L-hmgj>V2NQs^MQueH7ca z9|w}ielwb;iA38NQXlz-AN}k5XGoYDs?$>R-LUJ0e#MiJyTL7rKbv6>g;iqX`-9t2 zg}10PV}1N8ljYTXJZ^=^%({sOj=8synWJN8;Xry$ng@yu0nSsudHStY^-n4HL>&** zVqL4SAk{2YJu5+W_Kec`OZ$ncL>}zv z*rHO-)TKz3N@E-tHZ{m*(NF#(*V2r0`3iuB$gmw0Uq9>VwG-}txUNKW@#5k@DmrGh zK6)t<+9;*$CRx?w!LzMtM6nu?W4RYC0`V70w#`%4^hH{3*)8n%0^ z1)?W%Y#+g*WfI$-+jChqSuc2tojm(oqun_@Ssd zoopH2PKVqF_+T@Q>wB&r$rc^8jjC+NR6oW(Z7#9p#?lH|YmFz8n~qJJ)JcUf_(|`@ z0Qi-671hOJEHnrU9w$YLkdeZQlUz4(RZymVGP)A&oxw&pxck_M&;0R^RfTo894?VW z=P- z8p^^kzS`0*w8gF6Xi}J(`OEm*PXjHeQTp9kdRM;VH>~CGof8au2gbt) zwH)tMPmpdTvWz>I4oZ+llsbHVW0j=ieNW72XOuNEj)Eo;WJ$t5&J!jgh2h*kq?(B->i;_v$f!LuQPn9Ok!X`!6qD|?ct z8(eb$Q(c_%#&e*e79*mr^@;9#qWVQA7jB4Q8vWQ5%|~G99>2W#S42(;L-)m}q92?E zUk%4b=oi;S3!g`XEunhzF{RBrn+l0P*YM1#HW3Ou4mkS@3k>WGeDq;rS*i5IsDM0> z&+&f1lk3!o^z=dJIq#bpi&r@_FIiq}FaB+KE4I>_>iO{cvS|OD-Rc~>Tk54ICk$Tr zKtYvP(LyzoCBSF6fvZ4K9@+x6 z4^Fr}+fHtK?&pT?M==2Ce5#AA5W928CNJ1v#~H!^dP0J@Jj0<0z1DA7=`{l{B>GtF z{$O1E{S_DQEXm#YQHoPJ#-hlKJq(YlC zN{K!SSQCAE^TzHbPjJ(-|G=@# zQs~_?`MQvM=E9Sur5fdpPJ>0?wk1ZJ+XzBevYCxQ#ULJ|*89FQ#4;zw@!I7ni^B1# zp~|nIb!8{`!niI9S-8?6o5m)Sm6L*5i2XmCG(&#Q1{N!sLpSzByG<`EcQ%F&KzXbr z54S$UJLK_k6ECeNo_lu%3yr@qeKiwc51R#r`=-Qhxq^>d_)=U>Ac`N8TkFeqJKZ$viW40>w0KY z@WbWTf3M~%Y<4T9zn@os1@iSP&B2CdxJs%&VvTvI(yvx~iPEeq6j|XmRXO5hg}Gfx zT-!yW#5?wO=Zp7~v4OX>C$G^(D+`yqkrO*s5)QC${KvZwff*}5=mg(+lq8#)%B$h*o}Jj#HNRyA zBE3G3bLkJPk#s35AxqMm1Fe`Y9&tm*DT%V_F$q74MTh)2k%bzZ1rh9JP`3Myu6PP4m-Q6W1CEeYfm%iV5zvnqW;1BG5 z&g|JUYi8Cev=w^i;^HhRZ?9k;_Xvw`lxrt4HII9JYAK{=`ZFibC~AzU6%_#VtY((U zUtjT6G*fTj*pz*E7-k(v%}w8Cyi7U|SI*W_#b{_mu$uoZWz(~5qqFY#MEt6HEu8E+ zY2y@Y-of!Q^=r|W>qH+otKDTsSXz+x@DPys%#PWc8hls?4SN`Kws)GtEjqWBi z+KU>+A=d_?CP8i~ym^nCAJooxlnZAqzEIjqGo&*(3zCWtEFxq|$1FVKn zZ&TT)D~MTpVy65`i{oI$6Q{)G_A@}>*l{GOd@$M-qo1WB=o(0Zt_Xig)RP>7&4mCM z+&K59yQI&6D}LbzUQCZsRyI;bV_sGFl8RasxL2jCEusmT*$z~A02ueIl=Z9(I}4U} zz18ReNRYVThYcH67X(?c;F8PzoFmJ5qam@ zQ&JbaD{d%_ki@OEz`SF&MC;br0$bW!{?KyN4zk+Uco%YT10??D`>*s`ZL}BaU2l2y zlYb<5Z$2h-F+u0!zE*B;*L=p<9j*&M=dl~ULe!NR*A^vgqgo4l%s=U|eg&W#DW-Vi ze+q>m8%Wmwg&Wf}4_!rNL!Xx&B7o6kTvQ%=UpdBg(faBOk+EDZ*H+6ugWIb+C7|Z9`rC{Z?QqQJHMTM7OrQPt z6X!VImu|n|vZaVO$dco!*t(imke*ZGL{1{@&f{$}#G^WG?jdRCOxGM>#{TT4Br|sx z=d=n5%35(fWLc?WAr~=A`mQy#M0ouGZ%A0_k}BHIuwou;pMoBr9^APC-{gFY0{>Z8 z=S$_$xOeN|45LGrqwLMbtZq@z{b^HwFBwPb82EjqP9YvQQic?>EW^Rf@*3oNHvBjR zx+UQJbedL)Ov-;QaW!vVxpFfC&Uzy`vQj~Qeeh$wM7hic+Hp{Jlh6Cs5nh~t(}_O$ zz0PegLlz5+$&+hW_>zT)-RKjb|(}ER}7(nlPYc}J$xS1g>>wa9^e{FX* z;r>33toCm$o3%kq>!%i)urCjw($%^d0?*h^*zLBG*!K|cR^>9 z)!UnNjTN5jEVnO$Q-W=PM*MRC4DGhwRL{rCn@U9z1;imt(})%e{#mE8w(+L4>>Crq zT7|q?Y{c|5HZkqY2B4rc_79dop4btzqRyG|ipX>Qs@dtS&}aLmms-p6E7#P=S~j3=d40*r4G?)xiEeNmV2y#|iIOoxkkifn)CPG4 zg9a^0{7d-6UWG0?7uX+9m@((f{oGDM`pd1=KW|)te`dik5Hl@~<1Sf77pp&P`#Bpt z4MLyC%{i3?C(Om0cQ)CRj;vZchQkZcfC4{-Eik2O5PijNvy0o)9+-aw|B^Ws^DR^ZbiBj>q`bDIQiHb1ZWg7+mrdn_f#+a?dhzZP1^Y>N5$u`lJJ(ZUvjW)x z?0&qPG}5=Ll^~A;B5;EzJ2ah2^lB|j#Yv4kc?4@B<7|1m-miSJ0+EAf|M#)HQy?c8 zlsd=0B7k(da`egY%3%E0&G)S1y4i^hzqR8@HXy(VY03(8J_$)E&ctNCu9i5!3->j> z8E6yPaGmSB4qy~szq){kLkB(jN?95M%80hsdLY4mfOSj&*3m5V+ezcUzL`;$=)x3w zg_O*<=g%=1~Xzw3RLNeL?9UvsK%*_{>}$p0YC>(MoN#k|G~4t zZeAj#e+<(D2i&W=oAY6|1w411b&~ux;%w4-Y0N16+&5ok2G^QOu1$LRo!Ue=pl_Me z|1!CC!{18Aedy-xco3i=cc*CQI9?g+eO@t57Xv8Oe%Jp2Ewho0{SxKYQ3^@PJF2d^1o_8&0g`VI7_`QD0fs=loA?h5GpH#{1Z|&Z+ATs>8gF~ z87YT87OMw!GMx3~Ej0LNhuUYKGQDPk-Km z9mzTj-eUSb&?UL3ckGnOX8QxTPWN>B_;mWnh}#13l+jK^?|Ecn(na7H=q4SKdH65} za8P!y*Of&3r&hoz_g1k}0)-$I`=->^p4wn1w zkjjZaA98P^ZBoW7KR5tizFlq;ps&l*Kv$4J|#N&=^ie^)cEhG zMOXF@>?`Ezbz44hL<`<3sdhZ>Su&tTmd8&0WW?O?H_F8(n&FZBT%i?S>uSpqKE+|D z&XXtlpzCw{6#x8o33+un(yM{U`LqnG%|}?F$&ED)-TN*9Gj6Z>BKTp6{}|3hOozX2 zU-|+8k=uvO%`Sj`ip#5X47lP1q#X1=#SpB7tH=REd4HNPi_ne8!MOi&rrW%hThh$g z)0ZC91B5P0V;Iv9cgh$OEaxhUh6AsIZ2rFPMT?k00kNYOrJ(tA{Dt??w6``r2|d6Z zQRI7OHuJIG2EwW+;rJGG|Jd{f1K(2R9jkgqwPbd}@DHK;k61I-LJmGlJAXo`yM_aq z6zH}dwu0XytIR|KBV!3Ri#mFS!5ooG=qpH-1f~@KN$QZV+9&g^eC2bYy0-WFiU$Vvuo(fX(C?>o~sCxu2x(EzoHF&mYaJum2CrK*HU{Uaps0RRR5X)tD5 znb%_cj>Av3Ay_aTVbX=|2FQV2iy95@g#((#GU&0u>}#gzKj$q(0G^1HBX@G(fae#p z4!G{5iy~$BP_6C&;*D{j-T`KQpx5|#7&{~Em}uCcNNZCteyleVfIzU2$R$3r*bZ|g zBxp`5WW-X1t5j4y+&Qn+ZNoS2jmVsdkuc0AX5aClHu59q_`!Cjl1r5o`b-jQN#cC* zoSLXL-Ocivp>MVYJJsfoXw+V_K)BM@P{irQ!-D$kd#{D6rsBAgUuB|_m zA-aq-`oom$P0ft!c+31!k&qAX<|bwKI%Vt*3y5k7y(#_R3K;%LAeNKZyny=^kg= zyCI9}M(xkZvR#mC41m)|3t(UW>Uh7{w$28q9X11vMqI9Ss=AF~a(MVX--~-Al$d1w zl>Y$y#_YpzKvtfnKMX7Iu6p5<#nE${Kt(dLfA@Qouqj4#(!lqvPmr_h4Y`{v)}a`zf8awIGn0VtHgF^mZE@PIYwE6AI7r z8#tJp0P~cVLhrxwCxf#5{e;GW)kZ*&Lv!W1OBM8)%Ixuh(jHKgRVX5m%9*D5iJy@? zDEYJG@MyLn3&T$A-PGEH$mTxp?2{yG`aRDnL-kc~?C)ae$2w=@QvG50 zN`f;+cq8>M%W4P!5d7pPqk-mU0rhZbW`%h*tiDMmsPDSkGS(05Yh;_mR-OJg=eGr^ zwKN1c#1DoFd0Ih(Y$nEvuBkSLlf&35eJA5tj$wVN-Iiz*)Vw?OM7D%#nU^!z>kTid z&;aX`IANaFa;1*OIK&RT2X5*a*5V&m9|$55TmojL24d$oTcTQ{DT*cS$@m2$F4}O|MeCkx@%Q4Scg421Kk!<^5sR%A_e8|3(9%uQn(}zOeXM~M}@^ohXeGk zRm4(FHqKbBgZADxSH47-U0Y=zL=XjN6Njda?3ASncZD~bH~s+hO_<*>-SZjKZ(4nO zbvmf&4v>MoEAH?HI1RwtxahJ{o2iz#JCpCqb~)YBEqmt;y2t)=YtQ`(7__8)eNexr zYM?uwhFNY&RdZ zk*H8O9X60RI_k60qR0SOFYSzyWPbR zwzl$EMiTv5Q8r_rB_9UBV~?Yli_vR@Z^R@ zk{t8A|H+QC;oB87@G@kg>u;2_h{5aZ_Be0mj1>^pf-NKZ>)+P&qXubW!oIUgB1KR2 za(!~a(7a5$q61^La13BeAG2z&YM0M$cNN<5{cQY!oUCLo3eztx?s$ROs;RIBzXcp-Y3S2BB0*M}3piPSXzZAi zx69gjwezyq_IMRuewFxpmnMj#_{5a1Y?IT6lgGb~U%X#T7dYNFLyxb;OkWL!pX+_r z(j*?!KyRS|hx4yN z_m%Q*`uW9!ju&^tE+1@+)`!_iWPse^?JN}9}Od(S{C5<~iT3bKxG zf?YDZ`Z!;3!E0B53R&QP4#L}t&6#OZ?OiAh>H-k;|?V6804A=sL_65{r7U$?wr#uF`Tt?Hn87gS7|8d`QlF1>bU0& zedaWO&=cM(P`08_K*4hvg|Igp!JJ z+lynXUl)nqAf*>RQd2sgmA9kxaK4PWZndAxIK2HeE+>p&6XveMUC+Y!QSZY zqn*&eskjjiG~dhqL3IqQY?rq3NaO!yrn-ATJq^u-tTP z+9Fje@BDvO_NSZ?HxYkhhIi5(;H~)UtqYeQ(jsWo|7#94%b!X4a4byg)Ge?lLlG0( z|I{<(1!V6Pgai4UTQ4w{SBmH2m@Z&09Cx3kA_cYq457WsOhr3@ey4ow=y+_%+HKne zK*B1r=34gu`?h^4l-^s>cS#`42s$|h_&}V)r14mQrrEO(3h<3;oL0*OU-9kjmt?~E zUto_u!ZughGMqdX;Bqhu@9-%5bG!oz|Cy*R2wUO{v9F~s>& zq+Zg?8I1J{&7D7o%C>BbV!8c!spaCM)ZAdQ^knAhWoCK`|3;hjNyrLtam><0zPo&( z&RCgs*;uK{fKnkF3GYA|ga;GSv0VmVMhZ;YbMEgEKK=N=CWe~FJ=ieiz`r(8*f3Se zKS(KHPL)x_kIYeI9s9j}i3>*s7ci1YeaXjjAXqS#$P0QW!|z%*I4#5P$Oy;G5v}v1 zi1zI5_}7xE#Q8Ykx!$kyHw>x{J0WCa5k&_dFAmCn$kVj zQ7&+1O*pbz1K>xlhGk&rm~9IXbakfRXFogvfZ$_7R0Fw`){k%0vpZ#@luw6e9N8YqSFPqSj-DsM`wVXLAZuJ`HF{+`22J~P~rUPk|Ay2=x;`Pn;2e}J)XUKdEdz> zyx%q`X3wlf69$Szha2Cu!`dT2+lUh;)zrY((PKUyVqG?F0H(=E!u}nF-smiQX0bP= zG?jJ%Y9NK>5)lN`4rzTA6Uu`B34h%8 z{LdCRt0fly(T9OTP#cQ4GZ!JfWGR8&jM^<@o18#M6$tfAsR2|)ZJMY^@8Ec5_h}qF zeWEHOj9P-uI82HKH^@j;17w$Gv`NdrnTMzLiOY4oe=03d(F4XqEpAz1s&y8|fieaQgxcW%A<)~bY!b3OY>P648r;XAQD%X z;bQssp4+sUF74A#k%{&j6*M&cGFvW=E7g@M60Lz7kq%V|$vl8-1h#_O?=t)EcO8Co zvFhf;7*YI{KmMh0GaFT5H6ivKs1*4=%XY)1pwchTQHUEM5g4#hPC{DI1VjJ<5`+}i z3C8`;iFP#(g8;3$JT}r|-9jCcK{CO-ei7v53JS5X;i?Q4p^82FCSobg4(M_Iv0;>1 zXgWNfbgQUO0U%~_yOW8P18}=x!m*j){IuUKAacx`+Q^1sWd9GfIOuBj3eap{CG`}v zENWttoxrv|I=1xpG+akhL8K#(_Vfc!YnTod!86|6rtWZVElN2Lt^9I1O`lr4zHEH} z`UUg8;Q&oeBZ;l4xZ9s?F~OL8o`mxE&lbw_Oi0OveSmnC7@jA&riRkh>HYVN(^Jsh zDMR4>%YBHLVgEC^qu6Ul;2rssn|MUqbBO|@0e3)O$smP3LFw)UfF?GF8L3AL6LRsA zDzt#T_FkCPt&}YW`|O!tKJ421VFT6YcN_j5uQkT?EGm{&sd&fU{xgC1cK9I@XJmBb^y%>x^iXg+=f*LkmQY{dg0P`~|&^EVMzo@)|3dAJ1IGxS99?sYgs820LH9Tj`KV6qtzLT+JmX%lUe(LFd zQ{mW#P3C_7K%_%!_pSct`1@*A!;rn9FB(3=wP))@USA_fclC0C4+=2qY&+~u zU&CM?YG|9M?oW*E&m@f0?kKRLg@KqI#y&69@;6b6judy@D9mmRf#H(WS~MeQ!WEc3 zKrDtCXF2k=KwkxrpSr*1*?9|7&jMx3q9?TTG;vr76e*d~$=j^aMt1YkEe;04Fs1*- zZ+|csx)f}sGr{Hp0%s5CZ0k(Lkq#b!hhs709N1UVbE~khE5~*M@z{m4v7hLpXs>hm4Rp4Ijdiw z*mBPbAhmaZO<7;vvQc$;(gO#R7A$CX{?=>x)_-!Zk*xBOB8nLBekYHS=WiLeMzE5a zSsu0(UhG;=x+HC6s{-qzI3Qfcx6RW-0Jx$7}WSCZ%(629GPdlf#_ zn=u@F=UKrvIbO9=iAPq;xCOe*526^!$g=bTk!Qt*U(6LB2yojo5i>jvl$1) zdz87nz!i~~2sH4~03CW|Mw-jb;@f%5Q?R#R?Gxz@^9^h(E8!VMY(7%R<0o>jnK~PD zMMDF=vZh0_ttNJT|3#t~D_8RA9?=1@;eX4`1611M*1btqu$$juu;sG?!qr|zK@-(A z054J4z=H2wmZjrPeO3_lvB6MQ-wk}(Sc3n-Rjh-=>QKzpoXpz-RKgEJVZNlfj`;)rpLPqX0*P@T-GH5{0G#LE4$&);b20|J*F)On0TB z{kT(Ezd768djEAt!Pw!#H@aYD|M}6u<hi<5yL=DRpr2VS zEcUq&Y<6lkVg;|9{xExSaLFHT&XOK(b|!uWo-hJxWR?G^d*6hzNdog(TGX1e~R-I0%v;SvPEmYaA#qBDdRLW(+B}& zMTgQ?Bq9_ZcNMzXwZO+cmgQ%V=O#=s!)zzVnv46PR2#ITU=f`k$$}~Om$0gT z6rZLS`01*S&`P86Q>w0hAW4nvVHApyG!(bE)z>q5$3g@?M$okXF$I*#Wf23H=Td-Q5jTJ8X0l|*O!BnXp zTzxF=w~5>jf%msGFK1`Qmw!U^Zo#sf0(Tjsq`CMt4uOu7hr8US>@4+I^au%8I!o%= z;xBsHxdH4cXc@S0bZp2p*^%jb0-HqYRJ~dA;=J>T<4eR26PoX?x6aGSYD?;fasRa9 zF9i=%L+{5b7jEO^m2d6>?ZmYJA#(jRd?HP|4dVX{N_Lo@mq_~f>T2M z$_uezB3C~7?GgLQmNq;B(g^%I{4I($k#Ey~zmi|T=3|tTjcOVa_xviuMPZhzZ*luqoh@>GT#V(_ zF=u)FCH#+8n%sz_pzm2Tv;(rnN8bzmT6b$kosWqtgq=|USWaViQnp65`2 zOu@jI$qbLYap@2u)i`azA&4@B;!uZLosr{Q5-;mtYy^5eZw|cV2zY4+BoS$a6vB^k>pLiCO;>A8e>;3qaw?SX}0R0%)tG|cbDXS?1GH;npIj> zVKl@!5t?|JDvS}^J4IrIJmfO?k96P{|7S%qYLYjB_~4Cvdv&UF|BVSeF?~YBjw8yS zZmAftKa)eIS*g}H?)5Lae9j~=!z@cs$n5oUR}lJ2)Ifvbpbt^hTsZ!XDhfrGIimC|RG*F=>C zv1`MAcXPrOY#9Eg!?@xjWrK0{9q;5~E%5YW13DT@MS(Spo7z&7=mI|)zcB#bcfyf& zX>rHVXRrS6KSWM2fj#3aK&C>=NJFk?QI9Syk_`I#7~YsKc9SEL{s8UcZ&HVGS4^c1d*?&)9z*1~0?BbmrtHBk*8Q&UBL>~YM zL)Xm6(PgA!^Mk(r7S8uue%kQ5xZe*^?>IkJkCRV0qWs4k+QRHiX*xV*g>T;FEV564 z*z3lGJTpZi{*Z}$m8H0pVi0Tw%Q;ri$Zs) z$)_U7h4C-SN+2N$=TB0UhUqSSpVi>H7WHULm7(B=Q^^$-a%Iqu7a`omMHst4QikU{ zF-<(EK*x0vS#%FI(9jf}GB93FY?vW4QDS~~oxT`Yf!y>SMw+_7LxRFMPxs zcf;F7VBLL8_%AC*@Ho$o5BVjrw~O-_YxiTZ*ap_4s;5rsu#QD(NAQk-aTb4Ab?7xEb+xeGYMq=ofB9)+4tbOj9YpT6MA(`T!EG-LyfwRW{I6-auz!phOQ z-o|{~oL+lYNR03BF<0x}txb&Wszq@!Ylp#`wlY$)UCrS(x*M~>g60IcynG?h+2_!< z0d~50xfTIgy?HYh=(LnIJ<9!{9Z;J)>63wH$nSDq}g{p6n4)Z=?+1 z1qd{xsY6jj8z8%06f}<_&PF$5+rjb^x@2%l%=gWA|8e<#Q9_HaZFJ4{gj63dg4%aw6R)?d5+ZnY`!-7l<&$tg*c zeTVKzix^SAisz@Gw~+)0vZ${HI}Sc@b`GqH>6#?$K8<-I`NPhI(^1(8_fEg^3mcOw zlzvkhbj+9_zIt<7naEC<_9%jYuoZfXN|%d z&`LH|4P@9r(snkNWBz6aKOGvjex+DZRUMqmWy{vS?DCf|Zo;L+9tH5r28Q}bv08~? zFEv6D{d*N+%_KAhrS6v#lW$L{P;~Fu%~O9O`jwI%Eg=Co1I0+kxZ)bO?;*#&NGn=1 z-s05w@wkFD{nE~i$d)W6Ium;2x7+2Ai-!qYI?d-%%xDSyT?icsJx|+|vH{l9@O$ED zx*U6qXXEFqYfp5WOOahHRLJeWe0y68;)r<303Q@-B^M4Gk6v}^X#q;($Hxt^XF>Xc zjItc=iw{cg)D6+Q+%}pb%>6(+>F0|bw~BGOBdM!@b3%J&`9#0gAgWUIdyn0f=c;qh zVV#k8gokG``oh9gcNe`oli9II%oE&1B>FsLCcsP|-p56LLh|^4O|=3&il5}2i~E>b z1ejw73alV<+E?Qrp?qw{`Mpj5#8^Q5N5WEcY*P&@Lk~4DGc#he^mvNQntGf4&RHT) zBMGmTvCyaRlXX4(+d(|$=U?B)j@o~<4dw7e&4;*>J5cq6+)H&_EfeqvznScD@t!)K z$+Ov61}?FX+fs!tOlg>shnAWTz0Zb;O#EK8h3XL-y73Ggu-E2=sk;2uEN)g58tTwQ z=xu3{yvNX+ooB!!k-f6awHyC$Iv8zB;*1}k>2QaSpU^%iso#Co;fRrAZVT@(VaIG0 z`+yklMs70Eh^T~$5i6w>PbC((`7EM#+tDS7m^69=d{iIB8Ae_9Ve-&n1U+Xii{HT- z3+Zm)BKQDPthD>ZO)MRa(u@>@O~~P?_r)Lkjkmdt>VU~HfsWT>P~)T~+@EMs+5}t_ zZ_gN6Jntu4MoNjP@VL(V<0TUBtSREbkuFUg0%WdkHiYg2H}NTiqM^&~AmPBt-uvE# zvK*~c!b{TAV&io2_KL>aw8^eLP&MSx=wF>s3w}!uosV=63T!@p__>}0WgNY0oio1u zF{?F{Gk%9x=_;WZ`*T3QC|fmaXF;_TO7EsOxq9ZQ0kpm{qU&c(2J39*m%Tw?f2|fV zu>IfR)Fl%8qC82z5ZTt~d~s0A!Kl&h+3Mu?T^8b{$SF%IbqisliSPLaeVv&oPH&Is z66(%7?2l%N=BgG%Q3{dnK0vfHNNg#s%KwRgG@LKEE)u0*VU7}4)jLJ}LaA%w!eAG( zoi&V|QzrOBR+pX$)wej1gHt>LPufC+9CfT$Loj^3_D`&YuLl&HaeL&EHX}NLpaSks)%4W=lCXN{EWI9c0Nw}@yTH}mKKR3b<@pJA$S(YjC`FZSMZh9j?___? z7MvD%in32$2+&q|#N!BcbcbEpQdoNFJGWqQ57&f~D{Pt2qbYdw^Up+HXuKCS1P<&-Q%V@g_*g$c%KI{A2k>O+E_BOPn(Jx9$S1 z+L0*Xa{(czq4bN`%W-$c&^|}dbw2osW%x@T|2(MQr3kk54{hoIOS@rmk5p;IS(;wG$d56$)%DZmi177g;sI*~ zyGMQ(VGo9hCgq#uv;EK+yi?%k@fd>Bto!F7exUX{|BxI4u{LbsFSA5kZ%m4n^md7t zsr3Y^^^Su5F0b{LeLOR5iK@P_RvG3HyAL;jr(D0=8O9Eb#CcH7?6o=mf~S$nrv+yw zBg2+zbU(bt*mc3x04=LV32^sud<^pOV6R&Db(goqPqgUuqFQ>!4q9Khev6+BUC;@e zNgM;BFVMUS(KkDxTU(UaMO@%j}xk_ z01oUZIJ1V^*!4Ss4Zp*xr|Z4|&%#`10_2X3perrhQ59+!l~~6%XZag*aj%MJ)1nCs zZ7+4kS^tW^1kAA7HY!SnUpK(}>aV*aMe2?wq8ID>jt1A}!ucXIVqGSByO3|V*Wo}K z{Wj-Cx$Y-3Wgjoy&A8UD<@UqKiKQNlBRPZ8X9?HdWMmOzVa7g~Cx?kh^G%0k?fqY; zb5lhCn*cXiV$puuBGk)c$W_ zeOEMSj;NsCHk@)+q#g-R88D`vEqt5eXH4ajGE_3eB-LYCf=Le=DrJo3E+&SDiR|J4 zud89HzGte=Gng&ur$T5^O6sJZsgy!$Lo@;}E6~xk>>wrRn8vkO0PznEO+hFQF2W%{ z=J+LvuT`K=${Aw0XQ4LOKqnXnGCI5) z+EcpCsGZb|s-s-&{?p}olYvd7p|bTkA3#gqOnpL}EnH}yc34y9U*ZsuX2gaZXy`Ym z`4SNV$O4-hkKnT(=jIoR9Euez@n=ZTqPUHLe5PQ~BO0BC4#`*Wsa*&K5e_8w+|G*+KEfcu=8W9^HZvGog9saz)(`q^15~pn(xI>2V{k zNHYgdcE1vm+g=n?e8)3_9MY}$mh^X6j{WQN#7t85$~s+JoaOQWEnI$NPjRD1)O74C zvW?*$+ghKDA@o!k z^1%I$W;$j^yfd! za?Wp}$7^H1`YWta51693k`W&h9Xh2exd!~)NVzCLpevYVA|TCAD4MwEC}&=K9{zX`NSzjSh%b3$uQ1l4ZA0C0lhtEwSgMCgBgJF+Qe+&{Lx{Au{alZ(740l!;(vIU+!VNX~$`sGXL?4eE zp6Iq-mC#A`l>O}0^;20B)9pz#V^_O?c1_TwZM%^ZU^SN2G#dmq9qntal5I3G@Eay$ zjqx`ujMOJo;L(VvSC1<|z~zeUH3RpGgS}Ox(F6oD-;5rI#E$3(-q9{{@m}q#zuiU( zC^IwiDXmmFWDE-*FxMt%6QAGEd1mH5Uw~ZvxfZ!i#sg zfIE!DF(<+@CqkxW0ZCIwPSbQ)!$d0A4k1mvk&CUKXHzu54SkU?UPS@p-qoX7NP!-v zXz$?jwttb9J}6`%CiG>0X3Tz+GGMR`vHNM6$_umWu`P)S?cp*=of3wo*v|ztVk`sa z5}G)f6!tP10G09bKW<(D_qho=t<<#KN5q{4*`M@0Wa3>}S&G^{)Abl+agY<|cl{iB zV9$o0@iZ$izf)c+J>LFS)ZOysang~4(jqa2lr&udB53n%f{9lSQ(9r;wC1J&H|Ut9G9&y^~2KSHwRx19JmKK1WaW2JLqW!^17{KNu=@pTce^tJ%>5 zQqWuVEj6qxF7g0gTT^FC`qOH&4ldSat^~*;KwT|4AA1f z|4#m12Cnu;a8*M6x%`*1YXKO@N3JY44f5L<_9_-1Z&}AB&JEX{<>zkbp?JsVHQJZs z)P99!(Y5{zJ`;r)IlMIh(tCX|U$1++0Z=hAy*d4~{;fmfxVsn=K5}p;cUr^25vi>B z{y$u40PM9E%v!zuK78-|ODpisqP-_TByA_UT*=69n1Sm4aQQoFerNcQoJwXa$f$;< zu-*HXH0_A1VRT~4<;s?>c4U7$x%*`UxvEyE`j%Aq?I45#1}2-an<3)JL#sNDT1Q3G zSY6YsQVI<qV5FBreq=dAXta&+;xX9G!mK3@JIzksBgS;*~)^}&;n+e2?! zNK|u2aGL?ftK%XaTgN_zFqO0)C^ezLT$avLD}@6X_OB!qSPu#q6xsy1pNax*_^X}G zBNRPEIVgm<5AX;faA<2VD%c^z@~ed8!XZ`gi9ZiAy+_@PWq#JHBNas-x)}El|G4l4S`KHP808vEiXlWf}-zSo@8f>bN-a`3MPZsef4)6PIKNOhKUaZPx9R5N z1@JL138iZuh$hLap0;+L1H`q5Lz;TEi_eNCuIo0DyjC9@YZCL?vU1Ck0rltMuh4eO zgM$Ne!C=HlNm!{JBh0iSDW#m|nM&Oc!gM{49=RPaADL6pI&L?&CUT9jy|C6;LMB(R zhIf!xdYB>?lEa5YBKlSLN7mtAccvL+Ec;3GcZ#ZFpHu>vHu)T+|HL@x0*e(Sx+FES z>-#4fKJ;ZcFS2hKt3j9Yf@0~P1f}UA*J7Hvm|wvrux5U-ws7D+)D9BgK=ZCl&%G*^8SP9uB^63Ap)+ZYuN`G zmOT!aqKlxm5C)&=HA_|a9Kh{CrCn%x!ZH?HO1>QMjh%G)R@ivk8-{Qmad}{bTh@?g z=^q6WeZ&2x(AQ_cyY!mInjF9m5;1>1Xs_miG-0j2e$;;6pgbjB#JTpF9YbpN9@IH5 zVYCy;0xGQw;Y2$$%nr3X5)*spq6jH1XP2|cw5J_!r8?3k7Q@k02$?g9t1 zVZggZ7R^uwX};^mbzu#aSXd1!j`Qeq&6dsUu?M2@NQMej=DpvEIe13gsSO!UG`ogW zdeYRtupsI~+JzbDv0Aja$);+|sH^v>+23%adF7-~T(yl!1_6xoq=KtVTY;XaxRb850UFXvilMU4_8&{e8`MvZF~#)n z0#>Pz-tfGjk(il*T{uJ=OMPJ$-{9Tx7R{(}Ch zd!F-cR=2T_?D+h9iXg8I@J^8Vp}^cx^)EfGwgnVD$0<$mNmci~)y|0=6LA_Cl$@I1 zpI>$H-J-!EX5gI&iU9_C5594hC~kDX>dR-5IUc@eR zf($+F+&2t>B5+0tLbfhB2jH{rQJ|r2w4<%b< zGp_8vwQ`OwabTdQ7TUbRt>|73#zz3)h{KbKTDP{SynbJ4~Bk3#yn*16lKDxuvFq%n7hjfh|ARW>Oh)9=o zcZUO{yA@Enqy!Ws6cCUW2|-$<;l2Lf4}Mb|d+u}NoZmTjsrl4x*fK@&$E-F&e-LjY zT>~d&BZGW&_SfMBo|qB%KhmMbZyybeoYDft;>*g|%mh*grCtK`-n@!j-O=j5Fcfq< zdSP5a)WSb))!4<;XTjr6iW$?Z=Ag7=DlJ#{p~|FVQ}A}n7-Qf?2Ud!h8BoM9BZ-rQz&>yr$0tPzXOFUxK7ryNCFGw_-BJCY#$7A39^nOz(i z$IGvJlCQQZXI&;-%PTnSb@igrnHS&f=P0k9kdc$?o=0HbGDY!0FvH0i>Lsgj(X9MS z{Nw7?tF9zDyiNVsoD=z-sobu-5~@u(bo1^!IoDdOa$LS(<1#a^{nZbU;e>M$V7xLS|1ff~VTEGJY}yQ7^$RD6GaiW+@^1C^?{kyUMdw^d z?hah)CjokQNHEaQWJ@udcmC-534mE@gM7D6*ot}|Vf4vG7XqgspY8b?VT0r3)#^z- zbW*r8Vz$0WM2Nv0f9yv%Xh&Ymf?imN?$(I$;pCg^Vp`T=w$v5-7&R$}8^&z>b9}^- z2&^2l1%8hK90@6?>7mp#Ki!=0mVAHNC#45Wzmq1>5Y^=6w5f8i;^6i6X^>kK{5ge# z%%YUC*l&Z#A*Pn1)Euy+n}ulc)WMhF)#4F3EH136shh3XuUYHx`oYzTXf z+ScDxME47|G|c-wZ0JR`k_p0mN3iV_PmS}KiK(jiNY)B1e0bVcR}n0*?uO@G%so^} zXnf!Js{A*Tc}5L`Px_WD{Vr99P4T-bRCstPdV^kQ<8zyq+vu=HIM=o?w=3d@I`ro7 zNQ~4{=lnHJ1v6{OXCls}B-)VFqKblICkBP0-X$;DljV2L6ML`01;JRomFSb&_vEW$ zt^0Jb5kze3=xgqA$uEhcuf%Z;*mEurSeflebOgFVGa-?BvtBjn6v>T7(7C#wztek2 z7+q@LS{^`@jgtc z@5WCj&D*BWU;DC2f`_MI-xfR=c&>r*c6z?=Hfrd%F47H;jhjxQ^ks0NKfFb zD8u4SbCHo~u|&$Ih^Sq<)w&3FI5WDbUDFo#c$i-^d0%RrLI5`yf6qEDuP5#=c!(}g z@lNR&dvw$ddNKX7eP#&Rg0w|ekfBgyC<9s-Yz;(7V@BzEB$1iix5V^reXDFoHf?Q< zw;tWZ^()I~o~i-MbvGFD$bZD3B7Bd^ryqmC^fMcLLtu^ewpJ`TqGd9~gqwyXE3c%IrS8S-y%W337YV@;;+0XzJ^5mSNWNX+kB*U4xI8M|p8= zetw|d;{jk~7VKMdcsfa_8h6i!){!+8y2;H9-yN%pmN&i2c`{FXCMT!iJpJ?MC6UWv zmwzKMr#IoLJ8a&ykV~%2R+hr}sX!mv5}R?myowUV*mwFj>hSV_S(_s+qNH6fuRJfe zAF)Kkx;}DGBUmwAIr#jKbB4apsxp@zO)viZL5h%DEy&S1{fV}^tV47mh3ijboZOo+ z>e$yFI-Hj~csnz?`ywmq+V%Qdq0e1IcSvBPLzRk}3@Lh*WtH_OuG5oo`(&QC9;L)f z5R+u_{YGQWF)AhGnV|d<%h`!yY=EoI&o94ZndOv36zq5gv3W3l(@}i-v=KSNzV#@~o z-N|si>^?PY#c5~9J$)30W88qUKEl^DuQy0VBDu}qDBu>|M0)Ow#Crr!ow4mwK7VFb znLpzyXsL#Vnka3%AST1;b_sQW!(aKA*}_k4Mp>rNZ1>QKM!!9q6!C7|(3m1Sc{f9t zkSXwc#lhN3wd&S1E6y_vz0hy9@ZkO5eJlzkvPU^S0+dD+tPY-_KPp!Mmdt8x_lzQf z=6-o(Dpa(tPQ^}#)C~87?k~rw^adw=^2=OVx7{J1p!L2b*?k5)S7G7inY;v;X-V+g z=%~@=#{Inq7d#m#;}#PYF1$Z4HixAG#mgWFCGy(V?L(Zn=cC|Q)1S0qKrT8iIWX2^)$8;H9BXE z^?!V-4humSu?xau5qUAYF2Aw!7~t+Psw6k?Vn?R@3eFTebN17a@O1- zRJXUqc8GglDBA|3Ji{eo%Ur86HMZLMZvus;aT>`=%f(H{qh$JhT8%Te@lTV~GPgOk z0{r(h&$ORvjI06ZAo)SoOs|I+cqWwrs#{(&$KPdBl=TXO-6rDrJea|K>cXP9Yn0!O zTa+V)O9gFFRKw0zdz2Up3|h<#8C-3F44Uk79tn7bmif9C9HZ&D-#~M!_t<~ z=X&2KESV$Q-GninXFNb^=Qzx6v)e`&bfE7pNOWjxsB#TyY9K} zEi8&agN5|c?Eb#o?;a_y^}-~G!{^HIOoz3>QE`>cxh0`hRFfYk zz~R00aU!N(m!t~t5P3q85WLZ_Y6q*xQ2S4-c+9>NJiEn?gai-pk8JfMIhY^o z6>+3D{66anyrJsxze|peGd@NFd-3(t1?UOY4hLsx<<}|l4YNp04d-Qt?=p@~ zIL66y8rs?2n%Td09!_VWgMUb2lB-oo351oIvo!GnR<{VzD*b9Qp$Gvhol;^Xo=FaU zMq>!W+pUNB$n_#we@D8yI?{0*14Q1X zZ8z((0I8Akd$2<)?P+1{R~Fuk8twdf7^Aa0)Y}_=HwA0N+U64tPD#X?`TFCy`;e0z z>PV(%F7JAsZG-P?73iw(5*p}?HQ1KpbJ>6W!|8Sd#9sK9rIF%lh84cl2AJP^O|(jQ z#qD{0fq0@>cDK=VW=XGosQt$ybzz6KQXh-B)D^`^$JIBYuaRe|Pb0_YT5}|@U6aMW zojx}bmlRJ{G@g`vxb&*XYj=#Qgwq75iaHYg7KZ0r3Qsqerwp#a%GqweA6JUsK8nj3%e z@HMf15dheTsYrFl9ddqoWf=W`$4wl33oaLhCA=^DupQrVY!A-U-G-{wF2eRS%%MQz zQ4`0SS`-#mYPW(`nU)p5ng7%o(5?%KM&o{54*1rybo&U>Uf_+THGDV>^$!zD!{-*7 zj8BTrNqw5ODkmRnb>uFjH{Z)QbZzAN(!-_MM~N8fpoD7K3&rg0Qj7FR{q zY^8cQuxX1}Dj3ziH1b*FR(~>xO@j57vQd6;d8gcexVfCxk7IE)d}cyGh!S31rhZ0aMzahpDak2r9YACI zl#8?4j>`#b{z#sfgMedg94GdW7W8t=C!BOiBIOPCtFNL7;>z|yJ;PoG*)%ugv%wHnIzyzNr@Y=8OP1!5IvO z@RI)9^fu2MR%X&DgDC-lEUHTEJN(&4n8z%Z1&*@uNSYxP#pPC}HAGQYnN`)KYYT7` zG|${N^H9C(Hut)2b26y$fWqKJ>fvsL8HHksTxIpK2snR zn@>Wj1$^Z~F(^5AygsEmn~YH=tkqL8Fp-riK)ENfdUeq&ij^KDgs&gz2OIt7;5$TP z+NRBxliKL|DXD-CfFR4B=VZu;_tkn6*TJ&oPQmC14k(_xG z&SHG|V6fKRkdndJOFxgE<3P4h5h9AB$Rc|$D8{@Azo?%thA%_>L{(34)Q^DmcGCOUpu?aMe^w}~LzZ50|pOvY$-{pw7xLHw)aOC%| z=+}Oh4_X+0DN5Nz=%AMrxWt!m#FzXO2OolTI5X6UM|%rej``oQcufbqC&bZ1sqg5e z8m`3rmD$&Xytv=+h7}7m5hmIQLvi%_fR=)^V8P^CQqci4e4;!qkOIylele&Or0qA# z4Px%^GDS|OKNQt{s0G3u*>X1IoC|$Gk}9$hOAJ+snVr;{xoLzYcYH#q*VUi^h#kjZ%a+pMIcw@h9S7j{KEJ>)rFpY zR-R!y=}ei18LM`+JNJ^|Co<9xHx4hiaymy(t_(i%MxuXeQxv@4Cp`1hyxj`{Szl-2 z`z91gYMe>;6UaIwhS@>7=K`#^ zBl&5xGQg&#z}2(BCk##nP3>JW5)Bs!Zw)z>d2Om4H&OzD-F|)G#2mVE@N5X2^P)QI zjH7c6dE^e}*a(w0Mu%=5!&JE0h{%i79yG88th% zo`dh$qAz_dx`yAyfyD#2Sndjs&as7hCqi(P1B1mn?ENERr(^ElVClFm~T|E!te_d36Zh=}HtNOW@FVRV2GY{g1)eAz6Bz~td@ttemLR>&1M zGTdT1Ga3YCKw?K`s#hUEtKaci+Y>b+@WS+Y1Sr@zvRiTbS4iN+}357$@C36CVjn6UGsBi zIWU0D@d);!s5U^+UO+fnAwME1H%s6AD9lv!V65EBn0aYR4X>y=h}1$MV((DgkN4gW z%5(R&&^jHjl*ljB;<%x5_QI8vOp%b6N7J2csZTqGOU$H(Z~^0Xe~6mPvFabx_p~xJ ztl(N*XWBdbn+-9Ni(_Pj)0cQ`mnI4lb7mrby7(Cm2D=_-w;Pw9M#cKPM8z(q zS*!s(VlDbJR+f3QFZRDn0=-#TzyzhG#0S1`9ZQlAJgVh*C z$Ig_H0xp^p0obDt*&2tx^-AFoqREL4a=xK^$WzyGG0roZJd|!|C_~$xYK00Vh|9bc z@FgNbr6;P%p9T;Ho~?DCA3Em>h}|3LbhW^dd+MDFMdHf*EmgS{$Z7ia^vII+z(GPj zgbEzl73@T!0F$MNY*GEdb}4ysMx+oM@TAkVGfH!o=FnFy7<;}^ZVE4Xzg1wel(*rj zAeekP(2D9~6kJ{!dzPaW^ql-jba%44dqJ?yionX?EADk%eH-D-7!Tij1D8JtR0LxXd| zLB7TDI}ZCYSGnyxjA=^}0U6HR-dpA1DC6(m@Ags>XVk>WA({7Eaf#3da^_s~Og{ym zP*E=1ZlliEF@8MagXS+wn{v1_2huOAdj%CVL!JWk(`ljqvX&^cD(0#j3Y%*ei859! z@3Fv568Mv-@h!=KUB+v)BZ`s4|Bp@qRv(!B#=2R-yz`+yo`oN21#ae`bfSp~yw3rr zPAQ+9UQP)K8Xyw;E`+&okH)f44g!|@tGgRftq@<90>WoI3Yn+E`M`ygcfeB8jQAd> zH=ZI`R2yXzsMK>6+q5Ew05_3j$6XJth~Xj$j3@}h2Ch>-@`*}uSx zwZ?&geT43 zhd7*&4f>%6kofx9#%+{=xai#`49r}K(fnUCeB7Q=-W3RJC+kn}*9@ZQ=$-^OIH0pebn>=k?{chzetL zFrj{6&jN|W5Z-G}jjc^A@)U0xMTAfd3^{r=%X^~zdpr^Ab7%yH_*_+=S-@NYo!b3K zruh)NgZ}kgQE8Pig@Lbti{!nq3iF3h_v#l~e0sh1lsCJ41=pvo%OO|IAex$7`McfW zi#spWrQApv@6q3+wFe<{VY8M~uZHf8F(7d@^1}%5rmU#rKJ?-Cja=d;t( zbs5_cfamk$5j;$x(>am2+Uz) zZ5N9~BeQ|3{GB&${-j-r6Te^*0sX!7)Y?|NoYMlgm4njFwkKkoGMR$Ti@cWH6QWn2 zgaMQNS#;Azq{(i@h4_Tr8O|*CO0sH5DXokstq6Pa35j9Bc+9IAms*YQBoH?c;Bf2y2vErU&5}d9Rp38#KM~ueO&YE znmCUiiH7!BgcT&S;V0nIic)iMxoh!zg0i<{&S)IW9Fp$wVRYDELwHfbg_X#{;IY-vD3~5=TdM8Dsn(DE3X}KC) zHza2d$vZoKew>qTcc4H!xY-MB%=Cc%xg znKQo`c13&ckA=b}V4iV5eq(JxguyHxtSSdl4m)g99LB>jcZNmdom!0C+Zlev>O~)` z1b-oujb7dxZb$okhX!O{*&BD>mpurZC)Jw~|X5XWPd(<+iuO!GAs~ zF#69_^ziTzUY9iWirVZ+QI`W!``{TS9LwTdlfEZ-6BjIK;nlSUzuT#4F-pm=Os;fv zsO27iOv!#^bRTn4M455U`$vSVU(K(OaE6GlQfdl@(n4j$zM@4UbiyB~;QK7zHZk!VlzPHG#dtdZ!7C4a6_f|Nu4!)?K#ygQ#*R}Igc zorzq#SlOSN_QBQPfq=WKoS_cQkeiw@#w2#0IkQj#Kee+PTtGiWt&}R*ljD5H29YLX z;D`(j7t~V2&rdza+FIOY9`ZbF=x(PYqEO->xL6h7eUrtG#>581!IBWEA1nTCC%Ytj zk{5M9RzEx6=a~P5RC5a8j-uEln&v5Mc;l6#nys0G{l~{sc(0|`xvI#6QiH0R=J&?- zo|jfc?UoP#%}|DdZoJIzwq@=!hgw4c#)Z)h%jbc;B!lHB%f)hyysQP&#DH)TSb)U* zJ}7&VSy9WW(I_ZUDB00RtT8vd`HU~j>FuNR(mm|fCAlv+>@73ee%>Qb1CplCa(J8} zf1k~&BzqC@1#Trd%Dii;XmSqr!@Y(+8tFe`1LD@8n^;)uO2czX(dgS}Pn2|eYr~Nn`qoVf0X&2x+OIqNF{z+53r+^@ zY^V;0?6DXacxrO926FsV6;p@+NpS#aaiHNRa3UXY)&Ocbp2JD95nU4jSz>Z?iYrD7 z3v)?c3l7uQ4b{YY`TQpH3jDe_xbEzn%%N-Y;P*+62ymjtUOmzw&(PM_*8^XdHdmkc z)0^b`%zE-=%Be^-LJ|+cGo(he_>+5h!!H;urV^;UeFTMc5d4&yi$}5~zq@uo&i!_K z!PWSNESJ=cm#Hi=QffCyc!?oG7af7?1vryFMQ$M45GGZ7|04WKtpq@Xp1@5~!*M

    jR2E@I(Xf|{MW#isgB1j zBxx=oMb3fuKIOaRo_z1{fXZ)Bu-cb51gEOicD?>XtkoOG&e=3Zn=opb-209^Y0e=> z`czfxXG`u_;Y-W9L>G*3WlZ|=Df>}*p>lhR+m#k)0t`4dt-}eLee{E4fnR`ul|w< zFN6nl*Gm%o!9;a_rI)m;_^b1aUk6qL>xFl>yObzL=GcJipl+ux`Q3((Gu4I$JMKab zDql!$RQ`ABixK&7Jb&GE&^8b5~|o9PlxivfgEb3b?M z-QotmZ(r{EiqKsy+2I4yqzL&vGq~D&`k%9<*353|?1Cw4SCU{Omai)vqusyd+ScsJ zSNI^~Hz@V;J_Zly9S6um_mGyVM8UWwOxlQL?1ZPiq@{uoCR0preE_Y6JKj{#yaA}( zm4DE6-77Xg7vKI;_Z}#Hr}6W!b2~QBS_MJ*{iirbEt#J1vUPZzIlqGS{eYaE4%R_v z>plU_%bmwW+H|%}63M6jG~!txRl& z7Otamvxetj(K?iAh!jfnM~TI`LF;b8?)O>o`zR&sB7jQ#ZLZ(llX^kID3D~zFeK+* z9v)UY+@a5k<4ZW{ z@cT68PcHZRTL#m@lfP^^L4B^}_4tosaBn8cvGvVz68z<0{PsKvQFXpsaWaQ?Ue2D5 z&RX}cxy`?%p-VP?smB?gQMgvz0Xic4|7y(^da7#2S;40c=dSaF%jvn*Kt&{NPQAo4IMrXi0(xNyJSKj2 zwK_jLlW2gp`0#7bG96hR|2YXpPDEy`09MT)I=!dAWPQ+*6G}Q1eWJIJjZqpO)@2EJ z#;@3#P?fU%NA17sZu|@}kZ5eAS&t1)?ohrvN7nim`tbGOuETCe3C1X4;qNSEdle?} zyGQQ$p;WQCBh6IQK6)U0fp}~vD9x@mBdkrdX~K-`_Z$Gm>$31h$%8|BRpwRBJ;!9_ z{%y*i!Jsb}_V#^Qxx1MKs<;;sk*AbewQcr+pI;9ezJTLPMd@UnsNYGYjn-dUa~|?} zp4jpj(|&ue+Dz>37Bt2kt$WP8gv9^71(BzLmXIs%Oo39#6d>^3+H44NvGF)2v;?Fn zHzxCx?(2B0Mo!(B=Z890WkD%YlzuZ|Z7=&n|DlT?Bu?6qb4NHF=JP9iPmRXiSyTFO zlb1m&1$wHt0lY?5$wJv~x^R9`tvr<(41hfMIQ!~x{WQtj3*H9t^Y!O$E8e&87}ML& z-oEwLDMq6iXy$b7qvy7bBM0xx=HYBTiT!+(sB2#SN#RS$eFkqm>$GMtmKO`%7eYN@ zv(gP+wjF3Bbq8(VkiQ{zI*8cA>)6q&1n1s**`q>4CjOLJ&)sGw0HU_|gqt*^9s`}4^z00faLg|;J-?WA$AD%b|k+lWx8Yq`4O@6-1xv7w=g!*)F zUCIDRWh(-sti_InURR%w_X@IVkvTlb_`5~dGx?MpwteM3mSJH52Yf~b z2~vdeOVw1-2RR2wR_^DmI(GLqw>;oOnuatu2M_WVgCbA}tzUm0HeR?DU<85hOs%bQ z#bs&o>RM7NMHn#R`0wM`&#|5FC4js?6Va<$vgp&ezr}xN@ateft;3qft-4 z{BhH5miRSl{dKzy7z`$b5o$0^7404Td;EDpOTumyVb#i1)%uX>!81j3+)w8G6M%gn zl7zaJl^z7MC+!BjVAD1I9csnWBwJ|MQaeyycMie_peZmQD=2Gx?;GSpeDj26Kf+JU zoiB)5b=3#j@+YXy+3`>p<*3$=T5+v`6;SDh0aS4!U#3dW0KLj|;xlr|-|bXcRk~IF zZO%NSCD7~j+lYa~qm(`K+`u=OO;M)?=hsG#JKi2tS0>LYN4OMZ`gh$hOR#Aj!XOHX zn}EMo=95PcD5merLb2B@I$`xeFUFcxH9F}C`>JEoL$bH-M;~wlmen#rn~o43St{CF zPsjZiThym;lHXC%GIuqQPA_ojB?nH-uxl|x;D+zSfQb4AT3s(Wd|lE`mDsSc9sGfN zo>D6`*KK? z3H+6b(Ew6GR_}m4)M*5dH<*Iq`Ja}VM#h;|VG9`U+=~l}kJ08gaK|1VUGQ~eYRBcV zu~zm|`dIUi#Gq#fgpc3cHib&bZ%NBRClb(K5B6Aif4xmnarQfdtcr;st#jeO8ohz9 zBx|=*`At;e@>aRtD(Dyqk+a9WN+E(Fnv3OMp6<%(k-mNANzRkcw?_$IkTaFp{M;m# zf?qMxpm@}Z|AnskP|j7s0$vi!t_p?+_sb-5_X_tnD{L>WxZyJHJD?=Vw2l{k8(ecA zk4kx-X#KIf&Bepz?BN@5@g)M}BqgZMJu_i>l*NVV&wCAwoiaKn?Ldt0S*VaW-ZM+O z(!QY{v}yYFhVt1zZT=RRxBX#Xe;(Apy)$J*!DOU3bv^;BaVbRZ?hI^<-WUiNpu!EF z;BbiInckE*R?86S)ykFFLx@@;z~8{qc} zJ9j{XA7&i8nIj9xa8_Cl8Qpba*{BkMu)qaTwNu#T^YaRw)v1N#d-Qk~iF- z*nOT*m|~`6iC$GM>G&&3md~4$f=GM)cUXWtM>-sSE?Ika&eR=P!Aru~kr?GcxLvsy zEjs_u5NFSLK$2wOLrV1MAt5{b^64ag!t>48I#lNcig?H-FQJbgQg!&p zu5wo$!Gg~qDFFq*`T@vnaisBSi6cbT8vJ-0oCQoTpkaj1XQj=vtP6Om4^i{LgaNeI zl2i2Q9kGfdu?TNr@OW5uDy3|Gc0`bbRrTQ?z(PaF*+&I~X4McQkz+8ozNjP$L9r z3p^9~O#7WD;lIJuPW0%aTn9-*XCMExv3Ml;7i1&7D|^>Ra@j%*5plzSS_eUNo(u(m z^6LZKjy=3`sjY-=c@`stpthG%A6N78%<;$w6Tby4m6w{lXexf{SmpX$Zf1*9nz$sz zcq$4(BK>e`8?fQoTmyXsw+((@lWUZVkH3Fi_p)2>CO3bI=tjKep(-EL>9SLvuSQ)( z>gZ%MDUpX(Q+b~TkNZd?aW6Xbdu3RB*?Z-z&13`>Mc4yQ+~yUax@0Z_H4A;H6N%-m z1a`Ruh6GbS>b_l6#$ySLegi^FpmN{dIESYwxm`c(nl}f9z6qSCaC`1{h$Sym> zdFKCoKJ#+k%{}-1U9U^9{P{HU&h1=JWme9z)#r8Gbn~t?6l^$b7sfr2{_CyuNu{L? zp@pp}SIklbGRw(kg3_vB-7qjMSV=e$;z&y)4 zR^3c$FK51C>XU4xYg7>p%p5wl*TW|5(U{33Wc=c0qEDM2Xu&ooMI;5ZXY zlvb#+kQq4*wT-Sjz*ClSLLc`(hU->&lTx@G@aca8RrV78($A!PdvhFY5z9P%(lt2muq(ZSuLO4aloURUWMHZVcsp~g&Y z$KG_n{ZlPzEj<96O`6IqWDG&h)O--u`wIkZKGhi!?-grML1}y5%JXo{&Mw!XxQi2{ zw`H4MfM%@|L;~k<&B-ZGdYmoH`-#uKyK@r-Lbbpl#Ua`i&i1Q8vt7@w8^b|{?r+%T z&yFSMyw{{@_>S4Y{I#KTe7&J)N1)=5~pC8X8egCT+v1MQ<61S|wy)~KJ zr$Iu|!>dRjLGK4InFv6xO9CP)pgFb&(H_FFN#r=mHqC!;GydX?px_qY4GT8A5@fSaJwTyPl48 zUS82s)I=4o9No$|PuUW#w$Sc6d? zg1>ek{1Jm^t8yDEI3e5;Bw;@AoFgtWHD#zmS6A{)U03qS{-9hUvVk}uyO+O@XlAxh z#W@bPQ&0mlClB;C(5}#q&H4t8X?q`kZBPHXB<0drHdCB_MalBo;rgm~xcZsty=@8M z{1uZ()6*E9PUawB&Mt#UIo%;)Ab%AMVC)*W<+cN#;Eqs3Wmugk4LauaR8`6YMyWuG zwS({^GjxDn$DV|{yV?>F@qq>Yl^{NOX4wYdsMCNveipWeG>%IyBd$dR&slfQx&K^q zm344lRQdhSs8vgs7Ot)d|0h&uqLWr%e7-o2mz zdjUVIhKCUTVYEW^P^4AqD7?5Y(o~>%MU~Y;uDp!QjE5|I7=aQek8j5krJd{8oE0qa zG6Vn=+e`epopBnLtz`O+5PE_!W!h92dY!atyMy3)KF$0sI#*G3?>*Zcf7svpvgiTt z`;&Oqdu&xKHfVk;nFl&UQf@%Mf~Y*h$|t1SYKEv^@1#FJAI6$a+S0_4!2gas2>MxcV=s~`-7$KO&}WaM}rA^$O!wP-Bguy&?*^_031?_@1{CXmQl z$+ASJfHIYt1J5K2xu^J>q(;y9io=+fbujpcSM%~Dd2QWmyG8nkHPkT>S=I_)G+Pfc_~8yw<{EqOv^6y>EJSxpS)C!K}joW-agz<{56lk5Jn z+@?c)sr&#@W?R*JJWK5Qnu(8iQ>Fx%@zNb&>d6zy(~XZ$uMufY#QbV4^~ZY+7uhf7 zrrH?58BpQco~mB~ke~P&SGKm5OstMJlLo8_vE}2p8#inIfL-op$I0iNwCH>2qp15t z7CGgOznIy`v=SCM?Z!Us6VQ((M*;qJ;sK%_MtCFk-kBQqhZwPb=RF8=rO%^Vo z@TlEtAryD#K!{6!BMtKgr@j8;{dE~XE9IR<9Ju1H%CA|=D_k})w z;L^C5ebI#qrp4$k0&0jE9|teRrR{Pmqh;0BWtnk)NTLZnGlV#%`LAm3CaJnVb-I3z zjVa)3)(Gxt3&lbxXNXHUD@lzF0;-fmIcz}Ef6_q_c7yK92yzIeR)E;?^6;+KRTm<-36IqnW~WRidt&ZcSK?KrYf(-9Xkd(X-o@-JKVR6 z1utOMjI$fw6jK_qIa|neKG-$;eMeI3ldPEM@Z&Vg5UcqtoSwGWy5f_U6n63>zIXOZ zKcIo;8*|XPX@s4FnDKEG=;Se)n5c{WB{TlLL-iXLT!S#GH)@tlO+558veu#*HwV|Uq*p{R?UAcL@Jd$Y&3|yKex!*~>kkQ377}1?;&0`qp)Y_|xO;@9mOh z8L*f}vl;3EQ7I5 z%0a~tMHBl$VMM444#A;nGbM{p4+x)k&?|ThGCVpp^G4zio-#b3wB(<^g4h}Fhkcn{ zV)eE)Y8_;pi2I9jx6DnJzS z8@E%y!4fAB22hy46L!mzcpCk!=#(0>eZ#fIBI>Gugx&&RYiTa@p1v>lnZzQ+9yyXc zy>F$Z$nKAW%my)AME`v>!9>E!drLIA=#W=x(2G?cj2%B8(Q0_S0~yW6#QpG7Dx>&A z=m2rlwFCQv@NR*&LajxTW-2x3S#V=M3(K@+F4Hf3RN2Hp)R_;c_IF zV9<7i@b~xON9}p_@(?zo)EzUh@T5`nL`U$12)+O>ZzARoqNyn){=)um@|izaL@11i zYI!g0iFiZ<7_653_Z=WXo^eq*bO{b1H~=%OkL*JqtC}r+ny~vkW*0%mnBpyCNR(Kz z>4n0VTMoC4Kp#8SFLGb%dD=evnV(nmLE)%D2S>|CsInQeaDgb$Z9OmYXaIcl708FK z$)f2F>!I*C1Mw!(CHI>d3D`>&bPs3EaI?}7Q6|o5?H|Z>7k^_rh?1R2up^KDiwT1P zmEQ&qKfmwWxrLdMswR!df85FHl$O@1u2gO_p?J0L=f`WW=%ILKF41}-S8jioG9|NY zjw)VM#YY;Bu(vtC93ocw10D$R;w;4F97#W=mw6m~eOn)6a(~BE{XXUqm`8Ez$vWp1 zcUT9lfGuHEr9531T(?8jD25Y8b0gmqiktD1cX26brer!&LJL`6=V~5h&YsW@gs){H z_abFIvr1{aPk~AQre?NuthDV{K9ZcCq~_06Ju74KG`sAX z@MzmOe_nS`KE?W@sOTlyMrquo(Bfy`?y$zv z1#9~qI>!M}AcbX7R0RGcXo(WC9EDy5BCiBuCb*pVV*1d41_0g}yeFq}`W1y9pQLm2 zw@Dm)I^i>B;15r11y`~OT{l73_rrHUD2b9#S8q+=-dqi?;Io%3)7j6;?%;C*Yi1eJ zJdT#BGkbi>2MK8{;JT|Yya4Ngug$PeQ26>hLJy7s7Z>~@MOlVBi)m2nj3Jq~808~u zEa~>Q(;++woN$g*VTzTGRRz~3M&uw=wtTy-l{#wh=?taq>tTnah(B=jJwb^oQ{or9 z`RtU~A>=Bys8qNw1znO(lzg9+=#C57K!Y~Ap08C7iXrh`K$J>;^enulXg65}?}SAq z>+S&6FoXO(-=iP&Pd}h7c5GtcG5==rw84jqZ*SiP%Fvn`O3y5OTnl4}fA-sdz|pn9 zRw34U!Asdu(1uG&3d6BAF^GIH>!7;H$Gzx&W?j2vp9hkx@meXeh4L~D0UoFwMb{WJ zZXwKAOoHdBJD+x_AChPzns{53`>ZhGHEic=$B=5(yEj`axJSU7JtYMBDaA^q?RDS5 z#~JV8yp2ZQUqRAUP|@KtD_7Mg7LyMWBwW6++>pR5K3)`8WWE1F{r!cz_uWGAu#NxWPzuFp3cKYk&B?c*VXjx%zkowVC(pKNh5P zoJD2zTEaFyJWZE=l5)bD==YkjAC)<6d@s#kZ3_J|yyMGqH@~RI2`4Htn42BF>3eVg z7Fb!t{J1o|u%<(5qOX^MGu6<>-j9iY#xKh3!XA3ja!#UgcIb$d)+S-Q}1d?#Fsy~d?)9OEV!Rk z3zu36l`OY1eF?pMSqghVJEl_dr%e?by3$#W;ogCMFIr;2tvk1in14 zk_a5xf_Rh4an(JTN>>nA3kU!GB*7!`G6o!_puTr~rkfqg?@klWC+$LqNv92FiftIo zN)r+L1T>$^kBfx=>4I+*_h#&pHO2i+GPEAs$&m$T;ZI-a_y5i{jmDy?^vfG=XqOur zR!g$d)?=mOb!A58%nFRj><?X?*tYo_RV4`PTr75AuatPY@M_f!k#aBr7P{YSH7lI9`q=lx6-_I zEE7L)8J~88ji9QyBRiHwvVITSU{lK~^gHB13`8Q!xk{U7^Mg;_bo!#kwG(NxmXjT_sDL8l9Omc^^z{A@q&0&da->Ph=XL$`Fb4`u$! zX3re$Bi}B1Gg}k8uvQMFBR8ULbLupy;_*UsTGJ}KHd9L;0_2&}j&QpQE$H@Pwu8VF z8FYlbrOlc*)74r?34uUpEMBY=t#uL}Ez^m&6p(v<^RL%P*HHv}9Jy)|2TGO8M<}*5 z;^-UAs=5CAEl8Lq|JQt;mKd1=#7bH4iLQ!i_jA(JN(0UOfICG!O#xbp^3<^%MH_S* zcQaNfwya4hpI16)=Xmg#E3cMRfx*SFqn?wC=t+?j`!kduomSb=XK{mGad; zs__o+&Pox!(@#|0MudxbYmH-9NDdHxwTJnf1HBIZD*P3VAWTNM6cWSMC(S73dVIG1 z6=@GI2@Sz+NZQVufX;ZuN0JKo|L}TOYR?vm;e19)qzr%kKR7ak!QP_BwOCRyebTI% zGI$28`wHRj_QJAcx9+M`c{KUXmQI+4Df4YJ>=~RHyE9sAv1RbgVnzhpfV&N9g!2*R z+a&Nw;?NE-kMkhkv8sckjc36+o01Kfq_FKHQ2^(Dw3x21ZaU7_+ecFMFe?GKnR70o z^XaMJ$ad^4_Mvg`(u=BJEsODAb2Yu+;F|8D`;j`V#1(fBV9h&B2u=;e(t(hq%O95q zf6(7};{6hE} znf!V7_D;NfN8fW-F~Q?B#Z3O|cic=LM#b1Vio5=OJxB7))%PIyu;!Bvb8_hcb-{H5 z@98FFg)Vo_|%$#K+XTrSg`RM1@l zce0ZPV=Kp6%|s~Sd@%Pn*rFWy3hZ<@3@0q}kb3f!cE6M=Sj7<7?<9F&mt=*iL~y=+ zz+pPkMc12(zUk+9OXqLke1>|^+`T7&IaOFX5=tE>srYbaunJaT++eZCjcJyh)mPOVeI~RkeXKdXI!fwaQy|Ytq#s zNlY=+`!;JQsML(yh;526XFivR_1 z^zFX-JDR6)efv-yvyiiU!AJa)RtyXNf~HoOc1BYOo6)+uDw`$lgJd&jtXZMhYil6< zvfqAO3(einhn+4J9b+Pc^rJ^d7vCO`-Yr_8M%|EvJ|swSmqtdtE0fIn^Y;3upn|U& zN*4BrAROZnY}O2~o(unzCtdSlfV)UQ?tWfiCmlJ@05>^>n{Fis>H#eQ{B!dDDD$MQ zFig!q$KVW)kRmC}zn%HbQ$`rcdBbJ0N#$Pj_fRrh31YcwVJ0AQd*9g(N<}kJMoKA+ z)mX$*%sh#>0L0C1#sJjmFWj*a{w|N60ZA)Ir*DAE`n+2JMT(919gIZ}0km>_TOabi z_ZFpLKo|8Q^?XAA2nxViL%X=-=5XvrSmB_|(W<0OEHb;IXk^lac+>OEF z=kpv$JaLgmm~>Q*^MV*RD>31|`T#hM4^7q?tqG`4* z62+limv~sjYQ{LwDa=Z0lJEz|RQY|Shmp%Y{=7CiraWrA5z3V1UzBVUUvL-UN?gMS z*w`GC1)Ym1uv{@SjsUTRczx`rf>aMJ;KXcLkgL#4dzC%;uF9^DgHKjzG*A#9x|F^ zENCPObQ~j1W+Z>4ak@`5^g8dME0BMmkY6C`NDXZhr^aCF;qsvufc0`tIL z>Wd2*S*lRhuI1j2A2^3~Krf*tkSB?PKhTja@g}<)tGCKiqCjTiEg61___U8nn*$TD z^}*wN9WX&n9`x^Lot|~fAQ9W;*B4Uhg&*j@wW94bsA$~}KKC=|7kP{P_&O-U4 zP2=B30^#`0rHX`Z6jR=sQoSU{p6L$v+>Q;F-|z}WIr)1evIS`u2qAOW$`1Mglh zDZE@@D*6~9QmoYkLZQc6h_4n1m92r9w@D;xDdnHbcAU%7hd$qObCwhHV<`GiGQ)Zh z%s-VHV{exQ#=Tb|o|F1WQZp@$UXv{#$akZd}1j{Vp4h#9nnX19< zUhW;eD(8>94}UReImG?WegIV3&tK1PGiTZCYAk8+j*eWR?{ePwj4ziJbE^gEi4XR4 zle@sYbNjaXQooO`|)r#AH%BQ=gMBhXZb9{(gpM~qw!5wiGSAvV!ID>A$=-L_dxvU^Llx{3%1VRfyGYj5%Sw* zmV#H=;|rb!2V?fsOhLM~>Jo+wCJOA(EB_!aq}!c`@&lo7;4_@W7|lmnfYfNZ=&Y48 zAB6kQ#@&3G9?xXFb_{Ybxq>GKUPY~!oAYfiasmp@eVbZy0%#)zD(hd!jFOMfGXNws zZMx8-VOI6hvUfB5(OJM#3skQpy?hq=k{^U0qQ*8CQqFrbRf#1rS0qM5b*f_yz&Ykn znGhD5fR!|n6dewlJrA#U`CxSBCgMSRKJWqs95D!K6d;qJ3k*UivbSwkn_SM`O$ zRLXBL;+#3J$J=``<<`he8Pp$=fmOl(3LplHu6&o&ZinjGt`~f1mQ89LkfW{x`QF`V z;Pj8QXlW;Pp6#~6H)*57A(qQN0)W>P6Q!NNbxeps$fV~5+!;T$(0Zg8>MN_;X*4|9 zK3;3j97Jk$20SVZ!v^O@Ol+=tw%8oYVQsx`{5J^hu4qmT#DREg-_lJk-jDoU1 zNyaG?hTg{>?A`($BF8p&lDwB!zR-~~UU#BV0JYPPYw$AvBb|*cUoR)^ zzLY0}bbM0~D-<)8(bDgcYK!vXF(gtD3|-*VP+M>n2Ie0jc^?ex-5qE5{kk>AdMR0~ z4)H-tx|ovS6tR6R|F0P()?N#~Y;XTPVKPP@Hv^+u;h1<@)blU^tLJKc#yDmnB(6@M zYv5n(fp4^)f5;t(XdRFle<*BdL+sB}Nn&9lOo3i|EL#?(gY_e#>{=;mssAz~!J4^T z3G_d9Arg3Q6`#U6FI_6>`1G3o66=0V+L5Pe&$Y23MA>ge(w1n#CJt?Jf3>&d;;)E0< zc!_v8Yk1=|P=Ki2G9{&)xehs7KgeUF*y6yzy~C%$@R+(#??#j9;U}c85pusJBM5qD z^k8sI<-<}++Y%>&+79f8=ZaO)Rg)=S9I_W9N@B0an|KnNBG{NMW=TzZ4W6q23_UH@ z$3IDt7ph-^0|~N4ecNvhhu%rKO_*8Zf?&q%*;aci>>1o}Num0@g!e}z-xn>zT@o~6 zzic3EeVi5Y8~!M{O@6S7T)8KlK;Oc!q>&Pv$0b^cqq(0!73+VeAZbzQueA3S+~xMd z-IK&kPdUxj3Z*I4bu_nIOnLem*A@qJGX>?-3XW=w{w{gLY1nshTh>Bt0OdYZD7xz< z$?qqmk(HU{X!Y@QIelQt0X^p4__QxsoS|=BkTb?2>{|_XopFawdqfjY57J6=_Qq{R zp!*cU@^zg0n>P~@@)cWBezD`?9+7^BT$kK5g`1$;if9&qb?=v@Q2q8X4U*3*N97L= zE>=+RBmaTu%hU6(3isw6*uj6g;$x0Pq_L3xoj>wh6rxROFr}9u0U2|gmD0t{$BAwR z=rioBWSBMfO2^$LwJCoi2w5&j_%DUWap@*p1UfQ8DOs==5jui-X<@+=$ceyIWd>Be z!6mc_in>~cR(pR!l_CW>Jc;4EgL&;!e^k{(d%P9 zO-ldp7yAhEw9>4b{jCnAA&!$p6aUS*19NBmo(BJ5$i#~iwDJBkl>@-I6dqJe7uy%ivDYxbh{F-K>LK8HT zqS%%-40E1UMzbEviuczc81A_+p1Oud0eyro z@co|5d@G~;c)zB-gv4Fw=b)ya%_ry()YTo*bVe=TNd|j?o%`o`?@@9Yb3cVTQ>T$u zaimyTg+W25pJ{=h1$$6h^0AD$7%Z>J=!f?GzO)1MfICkWjTpl7;l#|JWIckbhw}_; zDR&htd6A;_6H)$1*#3o-wjBHi&t@qDS@0SG@TlYmhfBual)wRTCmCA3pI~TVrj9?< zcRzx->Z%)FBXM#xt#?i#pL4OLiDLWcG7dzsb8HtcuT zI%4cAi+57!&Yf zw`O+Hdv?Nw{;HBSGWx)4AK%7J!kd`pz*pLNYj|t; zi*dl<#cV&>|Bk{|OZm@L)b~lp73hMlBQKtdF4*VSYOwxukmX(ot=jt770R3rP|{P- zU8W(90mopu`QCL!_#N>o7o#KP@X`kNLtI2M49;~ViSn}kgR(pRTb1C~W zTyv@C8rI$HeY}Abuv|j<{5jU%4L*{>jQcP8Wq}qJ1U~r7aaTPBN7-&>itKYsz$HrQ zvhF)G%UXddbBWSPgU#hMbT#S!@gXP~Ak9HrouP?pm*W6k4XvbPi&Mg%h)M+oXSb2)nD_#;k-T z1z0zI;ju;`Zkji}ZRF5ta}pO-5!`@AY+TVrC==ao;`rM<@z>edl8cTAaKc#J# zevVwKkZ(ZcE`Yd5?fl35{D2?(8NA-W;=5W=21+hE{J5?poctG@|6*&_MY0}%KS}{V z_Ll;J_c&%xMKh4DpyI_4FXVpNw{1=y5@tHL(8Y_qjph(36z`#3kDOA*j(hJ)^|KQE z5!drS04;xLZEmfU6L5ih=A|CfztHRPH#h5--19d$%ORmOn+X; zLZa{QfCo^!EVh+`()gTzP&i$Fr1uVpMyyqlkb}px1wn*{hv*H++i6Bw!R{igV<{)U zVvOauf;j#VTW_JR{P1*5;~%rv%5_JsezI_|>#{?(fV<)uQ|ievEuf zO1!IJ1t!z3!`74kNf5e;tr<1j)UC*yZPANw2@Gl3$wm_|3CoB}zun~=T0daq8&}?Y zN_mP@ZEY@1M9DFEaMkfgRjVKWTh73VGPsO*%GhWB8AOq!&ER%afi6IToh97lbRB>( zXY#T7OG#&>Z~E>MpDLO6D%t)NY_cm!gA!;5=Al1>C%|JJbUipKN9IlyEqX2&XxDSf z8GT`dJJH^cHDmvr-y+SJyia*KPz;M2j&V=XW}c|>jzEcJiP(Fp$wXCP#Vh`Msptam zUDA8(5s`5ie3Rm*R)@Y;_dkUj%XJlpg)nNJI zba+m9q2FM3WX%*FyBHyR;$E5pC3Ap3a9g4D!`MjsPV65+5NRfF{RH|gnG7!#R@=P% z^7qB+?2&&L06Fk)bIhI_kN>pDUtZ9-yy(54ab+O~dJvdz3Qo;#zL!UCgPk+%^N?n5 zULQ~v%NfdpJpFff-0QkBfa_*4)MwX~L~8>OJ7xEkZNk%bG-9x}#6d^bk+=_-50OZ9 zUcWLYSCjDwwUjIeypP!ErkAJ_)IhbiU?Jrf(hlfjAA!BbD!Qc)biWi@+}MuINDK3d ze)$L@mePvdWI+q=cq$yjAI90;-`f))`iAr5X>){KSpzvUVs?e}&(b zee$KbyDf&bEDeiaAobQ|18e`@P*RaQulC+4Vd?UX(*iYG`>S-P>0w!?Dc{bPcUMPx zr{{Nt#W=NJcb&=^a^6LqKibta|FS8;)D$UI3Uo~~ZC8g%j3Zq$(hTHD$3>{jQ*waJ z+=-=}QycMIJN~4^DW%eHT`OgTU&iBtGm|m=*Y%92($dl;z1mI=U!aS))x)d5f&IKy zG|1)T6D*(0iB10?L4XC(g%b#|yO`iFz7 zRqiRlc6V=HeYYt7-yY+w7$&ZN`DXzY*oG+(&&Y&sTsGH}u-~|?)AjX_T%EHN9@J(L0kz=l!vj^Mw+MxXXM-V81 z>ht`W9|${x>8N#m5SETz&E0Woj554#lfr3i+tYpczdh2>YVBC9DdJ)v9uwpB=HQZp zQku~5IZo&FYBq|>r4nSJhoozl>riC%HQP|p{GRig+dIvjWwdx3J(IJ*1Tsqgurh@$ z_nED`7%DlzeF6LYTntsC_-N2CKU^K?!I?=gL!w92uUuBy8J2$PYN9ZJ9{h4PT5gox zsh0Ly23>v)mInv+@z0+pXh~<1+=zVt2a-H}?A(cNG1HG@tB{!j3OELjrMcVWg7c+p zJEcdrwMv`$KwVx0(b8UmNX`@yB0$J)7`;g;-&5W7ZWdDNj1-U42N^LIfZqey{oE}{ zHv+tp4k&vzdz+Mq!6T==x-^y7i)RKXL)YE&ec2gmKpXvAmxejX$Nw((8m)H{1<$iy zNQ&5md9YuZ5=S5fQR>oBX?)fR6R_nG&=)^Uc(`5)ix&uWijpHkQHFOUSK?n1r^*%E znyUgza0X;{@qX*T;G~`+Hs}y$!`KGihWUEG=ddJ<9M^cAM=fO!KVZ&`X5t=&Ri9s$ z3Qr!;Lc8%+N9)S{I=)owBR_sy$g3kB;h;)f>(rEcy;B#A!R|f1skfx~%c#?Kj<13B zyBC}?$;U{DF8r+;J7V4^J{|#}Aqp!>P{Q^g@iq1AOJgH#4zAdM`dk>L6!!0a-0`FR+4ko#PWOwAqnJfE|$|GPYfq6p=5m5$Fko52GSRmV_DKX(D zUEC9(GUh+H!)-|0-DTGo>002$BGf$@TbPUGOQ(e)wC1A)j5H&@tvF>1{B(CkDo~6a z|L=Ony1b@;b8erOPbR2ki~bZ{iaWKaX;nMwY8~s(a<=~p%F6Cg%8xk)uP#PSaOTni_|-`%eZ;BH)d`fxI**s1QW~{s234YeDyBqdDwZt{`D^ITuOwg} z$%;_N!DurZMfby?P^#oG>IZHgU01H0vU9uY-B5V4u;(i|OAZt!iB@unD5Wx8OE@?v z`pg_18Gm%9S;u~X{=WI(ekX|KPl7BjOYm<(x!rp?nwx-d7M$+RHFfEtR6GvDl zN`hIH4zr6C5yy7V;lk-tKMA2g6OU@Rf@%H5#t)lNiz`eFJzQwTe}ub+lps% z|HB-$jX2M^8;};cQ1N4}W}ux)C~{ z%b&yQ51V*jRWW<2E4q2X8ggAK#IHlgsQizFWLSiIM}wj7CnJC?@j#Lzj1#xZt@^bs z<-P5aiVbRFB5F1Wl)79a%Os~bZ^qm@v6str&(E?dtgXjvQr!c7_qxntJ&sI z!{)*w@yJC$Ymx$pt=dOLz4kzM=kP0f%{5?h%((_S-7$q}QB6^>adGqdy5YZ{6;vM_093w`;AyGYk@vg?IVBR=0Pd z()`t&!ny71prw+ct-KR}!``9`Y$q@5iH4m7S=3Tb!A%p;B%=HHKtWb!o&DwtOuu0C zwouWCGT|8V{GU;YQvVdCO@ z1t<-Og_Z7513A8QDVFYV0g2u`T66jm{aDi5!^pWCI=nS532?g<*Y`sKxuJa2DS~-( zF%Zg}5#_DdnD}0uoWNgnZB$DML?3Th!Sl#s-8!`OIeSCv!u9zX+~!?!$M2;I$})@1 zGdyjKS*5ieJ$~HbF+x<5B3HMri=?=AM!X)p|FXye&oN2~fGuu{!r#N^w=Wk+))Dy- zW{H)bsAn(r1g;K=w)VFV%wXfPw#{Ozu^SAMdMCSfr$~EYKIFz6Y+v{L2%m(A$F>;dI1Ji)p3A zid@c9k+d#`Pp(NZ6(dY$rmLjp!6tadeJ|gul+M#>$IComW&Fy?yOih4gV5a^i?33K z*(tXhDPa+@N8h(YZq)n(&DF9RQfa)1mJV$!KJ{oUR~K6EFkCy*-#qen`hg}jcjI1n z%G6@~{M3IxhF-1e5pDc!#H5WHmyCUk%28{h6qMk>mwJggzE3}6fX zONsFECC9VaA?PbEWvd^LidEsWgvI$8Nhjxisg&Rdxr zS>a=UuhW>?M8dAMJPV}k;vVx6#2YrdwWQ|l{hq_%7ipEGu;WQu-XHk&O__Z0+Q{pA zSJZa#7KspWD3eqGXH3jKmpI(&}a%IQK6fx2~vQrEHxP3(f+HxQ-*&Vd=@Yw*PaSoJd% zaBoDhUK*Kj3z^#Z=lvAP*HZW)B^$+4y2lUfy;0M%!|YwnKXM_Cn}gZ;o0Tu z_L_pV(JHK`uFLO9xhhGkQTVA$L&k5h4UPFsf$gdQsZ0lNyh5d5qX8_gY!Xmqt~K`{ zB8Q7tX!mJkPC$1nwkpkWo*994@G~Fb)*li<-mqybLGehMbRKDWDW+3g^@4)h^Q&wV zrqa)VVwp@m{(qhs2zJDiddS1EOsg%1lK*Lpzt7p)0@9E|b2%DM)>_o;`~-!2GsRCi z3Da#=GMq8!sl8WKH0V`tyPJE467M;_ZR)1g&xL39by(O*bF57UE8Bp*Wt3jyjX}l+ z6g7xt%2dba8UrbJeV_XbZ>D3$gc%0+84dHtdnPi-O3*;y%skVt+0+Wo7T5Azm0-;}EGWdMeZ#WTXEJ&iZo!(V_e!w6&O6R<4q{ z4tjEzT6%7r@UOtEpJZks{_m{e7hjaH92PhPJ6C04u1ICMcrr_A^r571KA0fNFowA@ zf!ZJ!w(i{C)*e3i)MoZW8ReZ7GPjIlahq7KE)EzUYV%C4bKLd#;n1;A1G6T|`hT0n zMRTDqSiI!d8Yu6m+ee^hp0J;=x|WueJ~lxrU!-uVAcLx6AWu3wcSovHtDPNMs#~hU zZ~DH-$C{1~!}%_&!CL4Pp49;9Dwi`^2-au>S{81qiT9U0+!ijvZfnIX%KA1L_x1R- zz_i|5n_>%NMpIqV+3%BTKvMjTtN#EyU0$K)5MZxii0O2tM%XH3o6YEL8t2U{vqiDu z^P@-A(FWXc=&v)EIzS3GzDxbh5C$M8FFoT2l=YAZS(MQ+16N|>Imp%6sjHSEy5DpU z;n!l)pGfg-TC+_IED`DJ*_vWaYK*iIoc=4ZeEiC1_ErUMxsI7pAV>S@N?#ak1|x7* zgKJRoM5Wjm=%f^)WWtZ~D9@ z`(vMWUf8=OAqmBS(oT(aZ?*=-BAfS}tEt>7$w8n0twEpn(5<=4A4N?Y^u+YoH68qK ze!pIV{;eC2^>Y1}ca0a0C#rW5DhLoOh<6DnMwtiJk%OIRgS+`J%e;z4@OJ85|AU&6 zuXl4XMM`Y3KURBvXcFYI_|w}iGL2}JH=$r{h)eD}EH++1qIMK*z%(5R`_$PUB+rMO zyKbJEhPXrih3cnB|lq6CN&Yt#|kTg%1ob z84h<~{cAEgV5{l*@SSe+3TBfSXiI0U{%JFTFG&X*G{x2+{x!&&9BrxSe3@T4<{}yN zq>YNM&opP57J|N|&}fMQ9cYu3V__l1OxdvFrnzR^sq0FS2`kENrn@TtiYNahcu{Um zN&Br{pP8@9B#UfbP1nbL7HwQVW9In>7eIYq@*c5|CJcmfe6)pUWvY+Ow*H_8wrS7kVYlF9w}*hm;Y{b2YOX(3LEWv^4`$+eBvRDNtljQ~;`g04_{_$Y?*H zZ^qN&d=N8SXX9hf5V9rniI@)GQFALgfo~;7(%fI7o7TAf*sO(mg*uHFp2BM2u-s)e zKQ=Zl=WmnrUZOwcw`t+pfaXI=xdcTGeHv7s!n%u}>XMoSi&J^pPA#$xI!(Xsi%YxE z))Ps1m!B>F5|%O=;0RVP?6aMwpj!D=39XAII!(k;{0Wcqa!lgY#Dmq!4u0u*YXk_@ ziXXF$Sl&9OzBR>05;CE>Y$Ygus%=4!gSuI9v5K|X``#@uE+DjQi{>#g0sL|ETH)@) zZJCICxg+EQ#EJ33;KhM9O=rNL&$d0z`wYbmMj6YF<*~5Ob94ho^5s2v zNLlN5kI#fd>&Yofog2hBW^96T;CJdkuU}WXF1NheH3QH^Z#+TYq8-dy;0OAl>9U+u zjw2_MmQVko5$PMRXU*II%7G&iM2JleF3n>cf;9eBo&EF3ze@_vp8B5gqL>`$?1O6H z9edP)>X>j4o{7C5MzEBhxVY5JD;;>)376ibSRY&XMniW>SUL!Xe*=#dqlKjSi%8~W z;N?{Z4wGVgLrzxBU+F$21Q9C~#AxfcDSeNqkhi2fl4j~%56Y-N-!j>Y*PHJ@LF-_y zWCN_pBchGfN5&(PKT(R;+PTnN(7N!4#%C82Pn-{4 z28FjNWU65@F4atE>jOjY_f8jTn46?6bJ2PdWvhZReUS6W!M=XTtG=r94E_cEg)~Q(gMw%AX*RvXzYrqhg%^!h@tABVl4PWzQD*$dc|-tW;<2<$CyzV+ch<1SoqcBr_s6~7{`K=pZdp<1D7f9ri%!obb@?pq2PkRKwzc1}KwOe&;*7=g|u4rq*oE z^9sB~d;ud^YDPwtH&bks11WpAGg>~qLd_?#RRim)uQBrdtM=!jdII1GW-`*5CW=FS z@sA{R)0xS>5aH|^-UWQ<@O5(K>k2KPgMGnf6E)6)D3!odaye(ked?(IeX^6jTMM3G z!QZw`dbFS(A9|t~KSG*eyJ75j1GIslOO7=a2VRSJSHLE(jAB({DBdJtzji59s}{?Y z<2&|9o)lCi7f3NqnzcJ7tt~2#lj!|avf>T>&&qGju(vOfHq1v~ogpJc9+PE433fToETSDyK7(cChK)v4VC>Fj z5YmUV=^?93M~gBc3NRd2y#+nWgv&`xT|%1O4y8?NM!%%j1G>8uvJIT(Bz!~a02(i1 z+@lY>yylx^J2`P^#|D|L@tcBi!~bLG%)?^(;xK;hGW*g@`>JVAT1J~_yDbt$Lq!oy zBq22j33aEvB!iHo#-4pFKeBeqR@Ontk|s+fVWiSJ^PB(YdFFZUbI!fzyx;eIKV+)j zGut6mVouerl-joXEoL|3pQtm3bw*b3!3Ure_f0i}on9jg`9+;Mb#LmAk@0u&hN>w? z&o`>;{zV6vnq_S(X$C>ydNoff1SA}9EFFBa}vmGN9>kW z(F(QwM$#`1{Ls5`xp&iccVfX7W5xP8cug+HD;;Q=)1YVRs)(6{yCEUKkE!du4jU4E zP_Ch&`sGbE_6iEplB&xxGwY{PDb=5OzGDYYPq;pNIbJ=Wv0&PjQ&S#2z3MD@i#E6NA^{1TGUePnvxY=yFZCFu_VxR9Xbg{c z3cqNY)ee}SziEDey@we&qmg^TY#OWY#L0P~eeg*c<=xwl0W-v+<&B3jKmJ{#n?qVH zo(10fY4>`fxfwyi^%2sKn%?#@Kk50d0k=8eqx!{we_9G{`z~k!mqgxuN_+qV7MAGmXXCg(0!f;g@u|1nt zKH**VLU9WjJJx=vx8y1>)}FnA6GpNlN092K%tKF4|B~>qzGSEB=W<6>rY-oO!`^nz zo;Wu7 z*W|W;nTx6k$0d%EV8){XmwaK1ljj4W(}1`g*dF)Qq5ZU{bj4;4UqC4p~I8 z&ai@k(a2+#DSYtKhGl&T6E>~UuO*l223&L$Zas_O6!kIfQp7HFwGtScm*l)=6BMTS zb@?7BF!w|w`-+Y+$UB@4nGQ}yqBeG3>j_9iOWvR%i@}3zDcFBC1qK1!487^dh^l5D_D=to15q5$B~p2*s6FM4s=d8;U5OnXSy#K?%Znw)u&|e!?apx%>A) z>AC#5e>tfw6zKs_ADw{z2Ykj}s_M5C=K5dN$3E@W0=2*Wj8+SHT_6e3B@pj*{olqG zL{3EAp}N)afkDd~AFBWIYepC5!?F#ZW2vbI*RC}%t`r3Zq^Roke`%2jHBKg8$A1`C zueqyd?Irv;b|KKYB9+?J93N|0`->XtjlbPN692AQi)g-m1~Hffs9m)c-_s(p;EeK6 z(Mk|UliXB)lHlLiLGsuyWAP*p&S-W!)Lo5LWyR{ZFdT6MyT2GL2T=61Mr%jQjv`?GgZp=5@AIm6VTl=>h`Xq-FEk@PAj zp~2@%>WKNIP+Y;&VfJ)T90X1TC2H>7w6x&iqe;T|;#89|tR0Sf++6PyZNsUAb|Lv* zl1w|U^5=_vrcF!5^FOf+k0U>yk+$7*B9#b;`rj+@u9iz3AgypP1P=syuke4>!fD$2R4r!3MS8@ueJR(Z~n0daGl~+4wB@1$<@0TF&&lhfeL<68*3M zCvouUug`DX02s}{Li4B41o-1&icj=Z;km->tQ(`<7j78{KT%QIBKX@#SEl>{Drtox z{gszDO0$1$Lbh5khWd3kft@>dYU*!u)i*V%h(xKj*un@a8$Uoo$gaept@&_-qe1DW zdIfaDrLKK-6Ere6RM>7MX3}5VMf{smnnwn84fvg<3s_6u^(D-P^?Zp$YX|(rz41~i zxh!R@D^4DrAb*8zOC%u!nHl;N1|UZh@aB?>6^?=?(J=oH4r_nitbn%h*DD| z214ui3g6WbeJ*0~bG+pNtWBcF#dLduJqtWM|@YUXPKr)yr#wbc>pevr* zD8~CAKNDVMozbqF_*$%ipg|dLBVNr$TwH!@wUP*mdfW1_x(Zkf3=Qh!&WaK;wmaZH ztM1$puxQ%g2hnhvrlHk*RQ%(I7)6HeK4tgsi6w66@El2_Weecj>v*Df>c^-82X^H5 zb&z5*ni~Z-9({YrnK^$M&n-uvUGBR|%!dCE12jM3t)qPDk;0Mb zpTUHY%|MzgRF!7pR-u4AXyba8l`B2ZqIn62x7;!bwkEb@K=-M6kxMXEOae4|)mV1n zZMMciRm~z%6F2x5iw{;73e?Sq4+(%3O85=tPL_OP{m9t{_4Iqr-;~lRo$SwHI302g(?N#fDc*OF^i(y z-^xz+{=Fa;vw^Y_wRyilEhzt@j*ak3#vf5R9$>Cdt2LVx;3{v^kr3tW30fy;LPchl z7IGph0c$mtM~huD$NQ9a4))b8+5JjI@ubXTdTJn@%?!ZG9J+9?sm0ht7x z&!ARyU}5($cg>i;L6?}fbP0`kPgo^@t$!nmwY-k7Cb?5TOm%sPqflO&& z5WXQGW=k4L8qg<Hlco*{`gNy z!gSenvAac8bim>_h*y{>7&-P?xo36z>)c>h2p`-`!loI9(lLkYmN}%RtCQ~x9;WlQ z2@9RK%?C{ehtx++ppvU^a$ZDLin-5+BvjtS)^qITVVr%?&L}PGmkEi~?de_%nVhXe zI9wjh`!a{8!^fXx$CnUkh5CKRp9uHd6=#0c$?3d6GnH<&Wmp`@snYja;3AxZVAWlL zs;=v^;Pov~_Gg0^7qz^I=3*$p`rK{b!36@jr`OF2lM;ZmPX+hn>0p@ioFZ`{(&ec+0g3|Q_k)K`%syy zRPs@zKb%0u9cvG7A>NAs!@d=_sN)_iYz$JO>mljUC3k*bGHbzZo;}IJA5#-a0+(0w z+LyonI9E zhhuDQU!s@;(*#r1dw|sE&%=rR_>3N;jEt;{^V$zVly5_BuxuwB#CT_PD{Z}>VFzU) zJIdbiFwul2zkP$q$9K9*+Sv1X>PIaGt-IIn6iO|gc%R#MV_ZP%?BpsI=QzEA|9UBo z5$hYr*T<=!7ohf>BA(zGwCjf-o19h56uX=hjW05hjf|9-i2iGPFr9$J{~RcNC!~*# zD79)fXu4mYA)oTV27RT0y66y4j~v{I`mcw)>hGLp@^2jxVgb-pquv)|`0DMn>~%K6 zr7?;~1r)7;*$A^{-LmFSo+uCul~_q9!8P}eQwf9e^#A@{^OGL6g=u=g!0V_4HqI}E;PC_OD~!hD$(H^C;Dj~|0dlUl|vvvC^UcgknbNAJqXi|$3g(=8}`g#k>` ze}hyw7*0&Eie;81bAn-yBf($^_cRaa;g{&4)}i~B-+gi#*ktVQQokKTK125AI0rP6 z4?11>=k#LyC6!pp`vVQ;XyZ-+*R{b^ITn>8hreng4`4;^B6?~umgO&}EyVXq!I?^J z#n)J3&44P~pa@^nM9uEHhRhe>Vbe6Y-)w6OBL62g#a}p^tcbyWfGi^(e)%;1Of%R5 zs*@WOFj=<5d%9FtOylF;i?}()qC#6<2J)Dua89AU++Z;v#ml_%bzYD*9m(GN!+&o< z%@zt5*FcqISjfwywYFj9VM{EW z8j9}a{E^w-n+4z^_;|K8KPQ;ZA z0_0LlX8&&RYB)V{jr2x-C!M%8(4Fhy=$mUuTL$6M46|lI3)$ngQpS-;31+lD+*4TAB<=+aXYC{bBO4rfnCBOl_N0L0;=?#5W*$)@q z2UT2m*Sj^hex&d}|CE|KGWndYJNBPrcs7v9_~Old--oog)T>7^UM1vufj(i-^)lJm z+`c*fmYw9idf!fv`X`EC6P6AZ0{8K5)nSk{Q)aJmow{=s7C9=IE?txp!C;V&`QI|7 zE~tM3*-fFO42=A9!Zv@@qu$!&j)#ixmWbR)53Pu6eq2SwE^}oPRaNYYWANBsJ8&L+ z8_!-c%ebN#nsk+TxPw9d;FD>oSot$B;E8_5{+C@KpXZS#IGwhejvwJ1`X03`Pt+x*g{2&K65_w^eX2adRHa%x=b}bojo)jF28@=IC zcRwu0YX~Xvmq6!k9sir9vuJu(k6!OLAvIAuW8X|{GvPypCXbpsjMRw!)DMjdPrh2o z{R<||CY_!HUp*6Mg-h|XWLK)lfbzd2oM|AOxOK$>Du~W8JJ}>LokC2xKFB1^%Q8je(Rz)&Y+ETpR2L zbOfr?-zhDYvNf(BI|2g>gcQI5tKrboQO3vU<}%~3b+ zLk>A|2_NVT-N4WeK6^yK2>@4%;7q&u__>2%g5!jtpj#&0mAXn>wC)+KdXxx0F?%=Q zX^u$7;%a|QeB2w}f6=fh5~?P}6vF(?m!dec;Ln9oCOGXlLN$|Sf>-1bTrjoi@WfCb z^^{siBZjqQ+`VNFkPMsFUB9x0nu9o%F7<#a)Hddc)9wy0FanFmBKbK@w1T$670?Tk zfi}5}3$0ApQE&v8kfxd9C)I6piU_j+G3z{RbPaqv^?QY%WqR$0#sVft^ctI;nDYpe z+#Q?ogjdvX;*Z9sa(UChjpF4qz1lWa%)qFernt>53k(l*I$w1sgb88#t`hfk^3cC% zZgBGETyr{Oo%Pj$jSfY#hLlBe?T;&C9_|7kpcaixzdK2xGAEuaa_h+<#G z0ji(_YG5g{pK;6o!{a>+m{yo0iTy3DkqFkViz5ecx{0;pVe>?*zg~f+ja}P13unUv z1s$3%&3Tuc2ivv-GybC7FFRba-mz!dhhCV1YR9}$rA|p$$#GWq@hW;NbgXl;hvP@L z=~~pL``;t6qS?f3&|fd+ULp0{1%s#bLS}NCkj|T}nyPxIvskq{T%E4&YNT*Vutg@o zVHjpZ;r}}3*A#4jWb9{dGVf1lhTJ4>Lj5W4G>Lcl zv@(+5+q01&?aets^!`WRc7AS8@tn)R;(&p!GlDP7D;UuVCt@)Wt>ccE%0E~0OqS1$ z66lYj%RTN9*q{C1an)8nZLt|Y9(zBxz?h)(An!NJrfHb|U zKPuvR2hqtqdn=$1F-KV@mPWnF!9x?m56`(mdSbG=6qG{e-`=0LKNYVfNYBCMgv&&< zMLI~#(|y{AflpepSl)?kw?}uGHO>f*|26)7wwzqzB+F61S{X{ZZ5Kgo=^`0NXkvuj z&3FQt${@OQsKlx1_z=8gsUGkn8twvP_TZ;*s79Nq=w+%-YH1$UohC>*^%4?5A%G%@ zE5cM+o6pWAJ*?*K*70+l$t#0wNY~PhBp0VjWMW<9i75~o*PIRfx7YrUl={T-4sKcS z*1=rYZLYnz_SaPFqFSs1k?6RZ{*yzcHeTiBjd6X;%6&)CHN&c|$wL2hqa&kbmzn9i zg)CC&W^kJHZ1MPoi`w?(+>cXM@HQ}3ER)#!d4US9|Hpsj#-1KpN}e9|JZRtsmjf{+ zk4CJ>vD=29BWWh@RX2SV@ohW_8)_dVE1sfG4@qUpH}221aAy z0mKC%Y;Hl0Gu1<69>n9%@&dapK*@v5xT1w6w$Us@*pE z{C5rM-*imtoPKPze*!k5Z5Yb0zH4yidgA3p2A!D_qg)ZkWWMkGqgl#0rK?-T|?nHd#_(;Hs>9L&Wsg}ZJyVwto2{%$&U(CZzpis#6?!`4Eo9R`l>dCHX z2W-tf!upT#ar7e?68}19BM1|n-!_NzbUB(VvEuxO7^Y-+^x_mlUxO3c&OfemHgM_} zH%zK{n)9Of-{SXE`mu2@JCTDU_=?!mh<7>uhGw!(UIk^I!mXm=55VK_$UD6MtifHd z+(4uVQusaxzkjr7j`Bm17&PgQw5v#&_5~Wh8vq&+p|*0Zl@lg}jXd~UZx?wfPnEO{~0^)LPT(piFkmTQ!+E+0Ozfd5JPA329=R%e$%l#K`2 zdcUWX4Xl1>37w!FKi|P5&0B^~szXesf{bsem*;H`hgI~u-IJU3BmA1P2ce-{$m za5#`;m{`V=-wt&Q41ju;Y&W7tirp@QJAh~s{e3+JD&A8#lIFg_Mn68 zqqkmJJGyDA0;2IbV86OhN;*G4#;lZ5W%g_BukYN%^%36q&FE7pMr;QilFjhpfd}mH0ykxu9%LipFo%RDdV;ErrF2xygpzGw1NvK(<9>{RrtrI$UScI(ee(p)ZlTRNrFk zexgR7l0&>RP%PE8J=?y&W{#aQS3Lf^L47g&4;5v)E67K)325lHyi7r7h(GttfRS}! zj6E9UZ*C>Yz%LR#xJIeE$Vu70S$1?rEpm4K064G*x;yo3*h-!w zBKeSAsznAB`k`akUO4RE&f`3m`iqcr@07(d8cX{qNFO!}N46{?xlj90wnpV`slF!S zuSX36CH$Opt(cAQ>PTeO958_Tm*39blLXBt$tfRl21os`?BYG~&UP3&<&gV5xo#~7 zJE)U$5(ky1S#wIE+x{Z6NkY|=G?e_N6xj7->)i@yvk*1U0J-YlRnEj$qR`#M{tvn( zmzI74en25MP2|cqfeSD^Vdsq3aEdPVV{j05-@N4{>ijK#0?#X#Y%g8@3yZaVNtAPl zr?_iwPyRN+Fq|tsO4pdtNnF6C!2Yu8BiX(dh{V_M*QiBSz7v7Q@tnqf zX3X%5|8{r&_lDi9vj5seE|V2L!(m&!*5~a$(;nB5LXkZ70q%mKhO^YfmCA+Ja;#ZB zIyRwI5KpJPpFc?bwdL>56IDROtf!s9I5_~y(ec?Z$fJ_O=0Uf0dArE-PXY*NdlEgm zIz`1`|Gbe;CO(OI`(9_;*VjDZDk5*YWfYOUw!qsMT;={EveX-NK*ztShmLgO{f2wh zN4Zey{!hkKIClvsbC@_FNoL3F)V&iR@QnmuWL%IQxCZplKFfF&~M;T9>|S7^&Ya zq*To6t+{ayNk0aCX(VBI2=)df(<`r#69stJ)|Dg2U#*zWXXltSYweGpJX3VK1PqW@ z7qAs~XP5!1)~%f!;`(#S0q;OzG{=;Ouc#-*y>@`xX?CqY#99-%3Z4(|eD(n->-0Z# zWb^=i=xH$FQa@&sERJD?8xm`mQSgV5{=rLsvty4P5pJGpEt#aA-vo|IxbAK%c;z>( zNt=(#6vQvnQ{i)otUr&}3p!~A z@;Bm5b|1w(Z}3U)z^%V=crG<}GBB) zJE(TFqq_r!I`6*}>z^Rkkx-@Kw_G_*~{vv?XEs{8uHhq<^*d@rdO0B6XwjmChNS zB}ve^%qPga(O%uk<<1V)e{W}RECIqb0R|xipM2HZe4g+RdMQZg`ex_Mv}8A2Grd4r zFJQTmtj%>sObR?aXfXa<(63%OALfwTWQtq^)wsBA&wK>gQrbsLs7lJ2Ta* zHxq`0H%QJq_RCI8@|s^L>9cP|bfotDy1bd3P%3ljGEP3yY8ZMKOe>?^TMRy8eD2p9 zOngdHe24=*M&4_8*; z^_3+mW=R3lP=I?uJ~mc_CY`S4i{njoDi(m2ZB?p?6uvF-+W?W+Q2gIBBSw(dfwlAA7ltImG7EkyT+S*MLfb zlDJ%g9@z*V*U1Lv-{}me?;a8zhdtK)M+Yq4^9@Mr5tFD((5o%dU%-<3>xYww87PfX z8+K(|P-=>F1)HD@(g%_t5w@i!?dE3^ac=O5&n(k50h5tXtZD0#8tqUA`GdwlhZHaY zo)<5;Ar_;udQw#mOGNg@;k5k4(41)9Csx>wNy#idWh)poCbEAVAl^VO9VCex9 zxbefX;f<@G;WOt5qqV;_ishta=GPqfIypIsdORS(oL?jKpoR$>d~+qORG%=(8EnPN zHyh=t&0Orx{`MwDWi3zeOWh#|odG&Y;rAR(vrRfsS^nBuJiC!zFjLwGETB~>vkih* z0DqO;I&eBi1CpVczOsv|9*1GMdmKCW+hy}QULF((8TR(YnzZZ7$=@Iv*BQgW9wS+=QD!+qGr0*dfc0`6yh0mFK#eX z&=8`_JOa#c7Cl0y-9xk1CV|7q+0Q^4j=%1m`vwH4kB^zJcwvaffy<=p8%u?rVw2<0 zV($fb^^Lp%1$W=_5Gb92z4^N&VZ;0cvkO4Vk^Ovye$$hIDr60bXIJa1P35;_4ox@@ z{%LEABB-1?g}qnq68>~k0k0%}6gyumX0fyita-E1?mcAUN4+ydpc~jciwsADfM2R* zj_|fpjpvYQq^0am>(L0&31}ILGbc^#wW_U^`=X7m2g0t%BWZ4B; z){n>8T`|BTUD5X^UUPZMl)AzA8wBPmzPq=mCUro`?sx=w=;2(Zat~R;fsUFrT{HLs zHPaNny;vO*Y)c%Smvj0JwsKBP4^v?U;OT@!E zF>EJMH5K1-jqv_=JYpR|OeZBLE87lfm?f%<-N?;u3Z}xy^xS$Q(yW1c{WF|B>ijE; z%tf5Lh7T}&?v2;tt->!f`v?9^w3QlVIaIGbs8w`73(lTMPi zo__~*Q$=54bvRv_rVXj5Qx&5IcfpDTa!l;NjCk2YJqSyMURM!|CQ}zUD56Vg^_?_{ zdWWuL<*?Q+o!tYjX-3YWgdiY5kCyn1cI)-&N+`X8+Pm!DUWVZjaVs#?N#XD;fEeVn ztpBP>&!B%-XvYEtX~^;y7lJm*i`I2qBDt@NgucxFioYS38ih_%E{;D5{;e{kf^8h# z01ibfnQj@1EvTo%Uftv!n85*zPU`&z9ZSK#yBi~wwzXr!QCW`q4xB3UK)5~BN7kZx za2P*jn-Mt~-7`UO8P;9O$lN&Jx|#>6_L!T(-!zyRrvSg~EWfklX`cADi5iQ@jsl@e zdpQO5mlpnW%LPfZD1a;94khUsDjr>itn`KoLZ$6Mm-1xy1(;0NtzB|>4vW)s$9Z!1 zM|I{eq|u0;Ve;IpheeNJA^@`0TmlcS#F zb~jXZHIY9MyA!OGBdqYnm^>;hem*`dwg3mQr^=cT^tcngn>bVo-}GGsHV@V4ou}yT zVp#i10V-gFh=B#TmBEm~aSc;p>lAJL*8E9U$oY>(%8mDn6ct?2-XZw-X<~I56qJvo z9dR>!QNMZ-$+Zo3$m*DDQris0Mh~WaGWVMxm?6Fzh$k?4whF@roxP-W#u9HZ5n6`? z=P8<$04;1VKf9G;gjK>2)Q3yqz$Kin?5S}72BiDppZc_9h1YQ;Bu+4lA7Lipyv2dZ zTyhfs9CY(y=j(}~mxBfj0`W*qBLS^|gkKW2L4L2Vf){9N&jBpW!=|+n(s7lRft$hF z8Mbe;zr&9kj}cO+S)+flp@IxF+j9`n(O;2)? z2s;WHgHDJKcW5;CmCtD-e3HM>xzV1{zQ#RP*;)XGG7#sK%Z zumol@pX}9f#}aGd9ApXwXM4@h<=j9>^8`iARb+uck)4T_cwlcyNd0^pqQw@Pi%izd z6H0s4k=3xj6Wb&=c&B`JKC6GeCMtmTmNM!-c2FxU!Tkf7NN^CZ6jTBFR7ga#zlLj3 zGC;lgeNfLMDNtqKQzo<)%_tgbUv(l8%z}r8YNDS)XJ`R$J87ariTQo7kd&svgN%_R zA1a;i)m!U?J!hcLJ5+0b9q_-S#Sc}^t0UG#Bd9z7IuduoLOMH}H<>~$?@)fN!FZ)N z>bs_jcD#{!)+ql&3j=kqCHP0ufW4$^a}LybSkX3D+R;p~qHg-BHgnODodH7WB9ID} zT_-oK81h=lMoC_ijf8J6XCJi}o_E4*A!8)V-BjcdMx5OI6bK*i4$5G{x^9VfZw2wK z&=NO=HqJB`Qi{%FWh_Sn;uS`A^a29**sVfnEX$qj{}J>Y2es{?qRQLEFhV;U2Bb8?OGT3ih#=mE=IZ@|< z+DEg#rKFMPDpa@ty8}xb5N|Q$Ps^RJeRs|9ejC6OUjMKn$b5IQZcU>8|QpQ+dBm3nB|b8g`GoDA)tXX89LP`C6)yM6;yh4PqB z{(AHi+LRVSpmO7nGZXX!#ve_S4oWw@p&v6S9ni)6_>b@Wz6z3aTS2*gsHyy3K*zwM zy(D25=9GOC-Iu*kpv&L?aD1zp$}{5;V0)&Xpan^J^r-!HvUuY!ZRshV1d&jLv!szX z#LZkqGas+iQ?!Dt>5P86IEaakHPXN);(!^nV3G$ZBaX)DW{a;N{1yBv*wVz_X`36( z{jR)-QSdXnc51>EicwFv{7*UIsEuS6QBP&SI;?`9}$f8jAsW-Pdrn+m=NAmoenPL{v6hND6iG(wwjp! zqzO0!7;L1h(ml&)sQYp8j&me3Sd2yPB^8@sZZkEfUB%MW6<9mu8HajiT)2#gwi9gs zn@?Sh;L}?bN5%}Qpx*Bhk>DF-0H7rkwWOJ(Ax(bR>$G^#7(1um)Ch#^)w<%EpzOK$%(N8^_%^y>6(@y3%)Ub1v9ZH%veysRUWV6| zW1Plp2S#5X?;5FU!60ykyRMJ|{k%$UFOL&GQOU?RqGBa?zQDDFRL?)X@w4fzeaAHy z1^mS{gEbQ4{Elf7JHB*K?7JG6jXT-Z)?~e=fv<{CmaTcwWC%kt&|o1xM}1-ST5uxQ z{bVN%bCO`dnCsb9dj}c&vR6DQ*a;kMH$hSpvX>m;?89uS7 z+Cj)&}uN`;=pPN4uQ2^eQLMKKtIV6e68p@ZrXP)OfIIVHiaQ6a zuaBcyFjD7ngPn<%67_~e&^+QY2fKnu9ff}CP)wNMgH2&I?K}jgGX?u@|A3>Nh(&?; zFKvz-U%V|u{_f=rM1K`M#8<%A$n?WR$FO>=qp%q~Br9j!!(E*9OLd4(WGRpMscyZw z@AZRAlcxO~3raH5l=no11Zo%PU%GxNZ)x(WSr4=g=f)Jw>Ik5XP^RTn-@n2`B1RBc zTwuAU@>nP@S5+`l`SJ4G_dD1B-e6-9+3aw8)z-n-Ex!k(4s|zZ$J`Wq=RoJ9xP*t0 zfC9KF#p1D+PE+YqBg{KFCnu~TL|iJ;Sc{fw%5l?48>R^hZwADOb*H}@*c3`~OlCLU z{hdzGvayNM1x@p@KVUP|k7b?XG^h=M;pgEKJuy`^xAZ7@p6^QlSqy0F8 zP^C`Zrs=RJ$!Bt`f|df0#$rv6l7QFPDtdBp!z4k@Y@MMe18F~qf%Qi)XnY>xd75r% zBsoLmdO=dI*Rnui6J^&#;=n4%#bph3sQ>&#i?Ir(viHlT!{CDAiiM&SNUX`yaUtGG28h?=n*Hn>7645?#9OAnx{^b7|a-Oq?v_;8%HtmzYu=KH+U$`W)7I!X(`R{oY3$IJ80B@9<4u&l z0)>w*dqt?cUl2HHrO4)IrqMc$@&RlR-+xd(wm=ZV6ZGxRDh_YV$k~ zYph*5`=RZ9qeHelT&8<$I*|h>^s7JX+aHtu;ibbzH8F=zs&dL<%k*^{rGbzUOP?gv zbfarBwcZV}bCc&3-KmXe%^L(d3`Xd2Mb1}Y6MF5#f!Z9~BaHAqkdb%1!+a&wLXEF= zOkS{_+VUY@4FjnoaRj-qt#Q}hJvA}!&KD+Thp*B(eV!;qM{eKDt8YobM{>@2(XSkn zZ@8+JvJgIRK4jNE!PqE3>lsQ|VjBU4q8{x(u^PWZE1yVQ+Y*UB4guAe6!PK1Ed_Wg zRrz)qG_(*J+Jb>g=up2-5oSIY+P49f{DWIz$GvpZo%N1Lsvd>Vx1U13_@jLoY?rZ3 zYxzR{)o#&g=uv#QrBBhuj^zoG&x$-n*>z-484(G0Yrl7@H^^_wO>%{<1f_kzBybQ( zWGLha_qP%+=|#4n3U{K2i&(I3@=q_Vl*w+r?xpHEKdyJIok)C9Cv{IaJZQ+?!LWYU zkVvjs&umJ^>Id_6_?x_B+5w8VZ+Hy!Ol^Emugunf|3V5;5(1(j4acBG3)wzuRr~B*3O+}jLpj{>Q znc@?glRGiA3Vm6X>iJPD_$w~1m2}< z+5$N<1lKqSQ~(wl8AyG4obrK)`b%r{le*@|9Ep2C2dTuVd-!rCK6P6_#IY|m&u_E% zg~WfAdH#sn{7r&+tD#P{%JVu;VeCWXK7-gdP)-Q{VsWxJ# zz2yX+5RmJ+9c;QsBz9Db? z+BfdMR>=W+-pfNS=Xph(uxEfqDsl!V0*-Jb350%&!kR6=2ZS2zlpj;HP|06UZLk() zWT}$qFQl%yCvT0lnI5~a)}jnr;(?+wDbktZ24=8<*gQk+pQe)apP zEv8#{XFq@Cg5g(*M5IBBdK6P?WQ57cStEFCt2>&U5jBpblQC4W5WI!o+ypB%Baoe9 z#;ww4_%6;?y`_N{(s{L8Fc#ygU7bJUDP#YcYF5BP^^PyrGAD_2#%{?pQdyJsRT|NB z5;rz1A5kp{$d{cYZ=7z4zxs4B(#5VmbeBcu=l07q*(sp|^9VQap9>Sx&3)B&)o0LY zGShZuXh<%A&$E+XXNZGl!0#+YttIz_^qgmHc8QN}V>iYg=gwIt>SXkvzDf!XtYCq~ z`p52qZn2+_n}&C+ltv2|lhj&T--rB7L#aMEkQ6|@kZU|RjP{;l9<$C);j|!v~{MB=FnzNf{6h+cuV>9~+3Ar*RZ=x`&nc3Z7RK7u~nyhs>B z8B*r+3TdEPYDhjsjl_cM%#49~kvUK4%!I>#1yoo!BNa%=4Xj+euhYMdr<9z=wc#oB z@qt5bMEak{CokV_P)CuZqz6<2ZFpxabEf#{86u|iwnmj~OhKR6VRN!0u0xe(SQin;;xFB37#&yz>rO+e zRxXi{D8+7^+?I0%yT5-sc}+Zfb9*>>@Q*V=NCD!$NH zG175;t|FikFZ{7%1N?U5h#BZ@Gr?!^?BLpo$(C@upKr5+(ySQNm@pyN8Imu}T~=;r zW&%~Q{R*h5qGZ%?#ZL_TpYaJR1kUk~c|y0d=nf(0Oi@zma>7FI8}Zgto0Fk6BDh5h zGsXvb-MN`ZpYZu_k>g9GR+Ad%*d-Nha5$XMHpgNP`nXd?K;$(=SzQ zzQvV8F}uB%22VBf+FO8cBToH7KTc9cRo(sZr(qSH&+?I$c*$y}=he;PZ+laVokA`I zy2!g!S|`mTHDl_N_M!;_BJ*`J>ybWI3_d|;P3*;#He{)`=qE=ZDsU1GC+xJ5ejGcv z4FfAW$?JyEct!fyYqsL;B``KpHBs?G+kOW4FImp{pQAJJhwA(P__=rXVHo?^hZIG~ zmff`^l+vPz7?P-j7RokvY^jJ=+89Na7Ah4*GnPssii#*BK2eDYF&Hzy`TqWedmi_k z^M0TA>-m&9>mX^LP22(WUvP`c$XOBnBb$wo>Koc_13E5Ty;0px=r6I4`6)wD#|W#U zg>gJw4q=8=@d}qSRf4p_eN?VN)=_Q?oa&opkj5n&k?p2`PPZhbURR14TFpHu-|Xt9 zS?)`BXjyChH+5?!yEWZ-Oum54z5k9#+o1$MkmvRMHjc@PLJy=x%C#2rJjJC4!NL#f zx5>|s(E_;r4est&@g3QVxO+R8rvdL+obU~Hm(wGhy^rh+j|8cI#6@Mx*XRFm`U}=6 z*E_u}N98F7TM;FUBsa591`-LB*5~bkBHV!Q3e)3&40%zzVubk)~Cfkb~Y~BsF)cCv5wEIZ$dmCp#|U-KmO!I9|9~ zcLHL}eYF-knMA4H;>lON%krhhRD9z`dlD|M26JExyy&dsI_af!x$^SH}n~6+^>pvp}JyTk1urxoSIc2 z#a#45dWboArC2K2`Sk~w94oa!s_~E-++;w1pDM49N+AM_vN?hpm$J5#w-}9iIq$*6 z;HzIU$*vK0&|2#dNJF&4Om6j{$rDBCG);?xF9*<&P1Ks-1(d>q92>Dx()|?@$(mDP3^Mp(tKOeH-MCH^c#-pWwsCq98Zrn64Ed4D=JVh)}jH zV|4H%i0*zX`lHat-HP$RiF|jV+GkgD#izlGHLDR9pq+t_DJpc9Nh*p>?a?yu2LDap z%aLeSDAeF}v|8X0^8nbGzLg57wKpwBS>AB>rNPW2)<@xCnnvbwwlj2zd;+5Nq`KPG zkT!WE3%gLV-YKXH(`08FBvjM7KaIG;LsI?BYcSacjbRuLh4f}5t;Bc2xa@FN<{f4GE+gbs z6_d)VU>|JL`!a{TvrwmuJ8ocduS#A5WV5hXvy`^7H)x}C;TU<7G*^{3_ga}JJ_NxE z{1-`9<6uC&Mb|Y)re=m*eV3L&IGks%vesF3Y=Tb5SBBKy&OB{nUBVh>K8m-z6IJ4W z)cj0J+Le$W6vg5-d-dcZwvhgNLJjI|akio<+)XvA$G#Qo_;HiRNIcX*$V%!h!xv zgIy?f{yGoNhaaY1L7%MY>K?R8%_t!#NfBU2X@)xeY5E0v-?8NZ=a$%|%l2k%koiS*8kvWPlw z4bLbXf6dje1Zl#y&70PHdH|=fVr7jxpd;|+4`D@c4sY^uT4}BmAI)v$wIZi{QtUq+ov!Qlrk3Q{no_Y7NFSSfh zuH^P0ZFmWom4~sWGd?^eZd6AXpaJs50dnaVaZN6))(niW`+pAxQIbFU!S_EmVZPt{ zVd5wgo&fsEFO+RSv8Zhp7?F98K-9lSw3zjPadJvL+|M|P&$B=WQxZd@_`NdVG?jBBCRoS2bfUabSO*z2g6(H z&8xq8i2z&jJ2i!xT!dN z_{Gr71(XESP4b8k(^sZiDD*k4#BMRG-gu)iVfD}aL1Ul_32>T_&vaeG$^$qd4+=@w6W^-jNK=$96(GqmFZcxx9DHTaj4 zkQ)8-L{%KOZ9PinYXkHW`kakP*g`rU0UErxO#Q8l+d!+2%tweQnPc>qXQH|=D4_-p zVQlpH?+!OD7Jxp2mwN4kw?i$2>!n&lR7JIVXBwp%Ke8P4z`oWoHrhwb`~43!R&F&V zji~xIe8EwOy#2i{7n_Vot7xhb2mP#<-B)-0E&cbTE}Y(NP3zS+A=#Lvf5Kq2D0q1d zblc5qg*7o7F%_=X)xen2v!G1|s0}0H1(I>e)Xi}Hpu>t%9nlkh}^kmzl z6`Ds{Xy&{K#R@(=K|5uG{V;S4>bUrp2pRv0sK#P=etFk09qM^xZ0F6*5i7g7I3l#4 zpr0H&q{4&T<&}PLR&<|(>FUsq`Bpm)ChsZ(5G0N>^Jm!&k0!sldDuP}7Z zGrVMd*5)bJUlPRLO?JhXy>2wp_<{3Q^=?TsBQor`gW1@b1n#TX=rRRd`-kg!9^wuQ z{Uo!Z^gPoJ%vIUZhlGN~M$@A~YXyVag{ozw>L;U|V^Fea z@YCa@flLSbaLP9v4aYt~AIDY3X^^viBic^7^690h$q_IpL!m#$i09_+%WxZzU@aQp z8~Xrc{=5m_tWG6n-x|q10(*+rmlFp>IWHT#Fh32br7Tasyj75UD%O3K!u7LL=Mpew zR+Q|6vroqv80dtxy4z#s!SlQ(4940#JK2Q+$RwGvMBOAe?jmw76zx_d7?_$)#tf)+ zeaUOi7<}kheuIEtEp2_%(X@9w1Zd*65_uGE3nF+MW)>SySBB(pdfVx0;0U_2Rvu`g zIIO?5DTZ}#yVJ7N9|_OD9hUODW=Q38{yL+|;6Tch0uhiAh4Kbc&trLD`2@X`4L*<~ zq*}2E&P1}PUuDACWh$F6o*3%_)(3o-Ib1{Kox6zUhT&SI!H+5na!mB<=#u)JGJ}*OPeH*{MM-GRjFCH@Y-)5wmH}7E0a>Sp7DA*+_o>ilM8jq z(raw|@dwF-;_wG&b(`e@RgJ|*&2tG?>Kqt1_hICu`Axdjf1s*fIr>k=D(phZKTggS z(9eaetO$bfNe#woJ>IidJkI@l3(=)BN`&pJ2={wr{C?7r|2qAF(~vc9(|ybay@K~TQ}}Y{B6n=aE4`kf_X1J zPf}20kLZ&DUaiL!6VVauf5Ykc)?{jH%SwY^484pTsW=%mMczAl6W1lh=cDLY>W~ck#s9gt0KKntO}2`jQc1&_1JpURj*(zN|kyMuZ+?qvjp1ZqA0;m zUG}&N?v#z$1O?Krruf}8ipnoB-7I6}Z(0TKrP6kVM?@pNK6s`J zJR$Qf)TUZVnejGy`@$b}CFZhFWFfM{0!e$#Y?eY2MblQMEU_&%Pp$fyFP?e^&_O*0 z;!=&~SWcnv@q~GM$+>kj_^&ZijbSeRTbv8l{wQUj$F4@(a2H=tjOH$~wx}LeQA%}} zHnXUe?Hig&*1iNK`|IeNNo9u~9P2S)opZyX(qZ zdpCpFGZifez848BpQ3Y_;FBLF3)`yZye<($!OyT=zVv9gVj)t+BNq5!>`$p6LT)03 z7o)-rW}Z6f{4AVa490?2deIe={l83e#(nXFV||F7&fN~=XlkWQt^M(`;xjOW!F3MLT_ z+$)W$fpC^W_|v@0R;$TBZ}7npEL&Q6m!N$9F5juG-DiARsxJ+{X|19@nPv)TJ~=U! z#DWU9-hHfjqd7aE`Rc0aetR>HshN&9gMB4`X?|qZrC*b@jbF$`! zYet|b$j0~`Q8%@a_^IaSs|3N35TjHll~nH7Fl=0%^xGpab>t;a+} z{_fC5?E&fReVn7vdyL*`wwWa%y_QYYDgclhF&KY`y%}Rf&QoZLfFwYNH;sI9v+i;E zn{X5bQAEy;I@+bV*M(&i6KZmW8ppJ+r|t>D?oTzqFazbV1fo2cZ!}dZWU$ z@|lgRg869;%3t*0V?IXqe#G~wrj#`+X|iT z9xTrnWFt!ctoY|%s$dK)q!On(M_XjZB`dGd6U6)$4Lq=+I(dUQ{`buR=;!3RPF!X` zWTiG{DHja|iMI(1Z5GD_=NMu-#c* zjvVSh(-vKOXT)G2`8a^be8~SSpS}>bBCj95 zjG<9p(PXW|%MAom)TNX5hEv%r*TT{5i!<-`wA8k`1bC=6cPka?Fb$jNJd{PwAe`6z zkkUDRwp=Q0^vHU6+LVYAllCdI-UP~IGM1xBK6|k9n+ghGwp4Er#gE<^Q13ka-R7B0W`zL%FuB=QR{X&rA-@5Fy43(XESAn)-2VQz0ZmCNe za^OCOxftgI6IRkm&=)-a`4Qmcv4S2Zsc%+kdzl<>1e{)JHiNf3$0y+Tf0|OH?~%|U zs7Q&FbV#4|gfKAe&Mzz!?+aCQ+%QQr-d4mIAWqS{zC$>X^@_q=iqzH`zSykXZ&)AT1}87}eWQh%{u z_w1?hA-LW-MM~>2l`~%=|DE}^OhVyux+D)pjfl7{XJ5$Jpj41QGc+3LA-ms6OkL~xgWNgnRgwAmd ztmHLQrQ0n%@L39&Rv04-;~#Hlf1X@^7A>SmbH`1!)r=++vcwmlCQ zMtAyriCE0H<{IKpl_EKc`hlv2J5{ zTgk-D_Tqh>*qF(^8=)5!rIIFh(9+>ki@Q`263=OVJFf*#*JWpb7LE#LqbzN=74ddP zvp}(;d6AF}tw_kX23rwrrN*2DMMS5%=#YG9F7;&Qs+spI!ZhK7#8eFMLQXcr7T@Q% z9D|J{qYqzrmH3%(mGL&WEiK}!+KN{x^e>-(IX_(&@fO#b*zY3uqO?g(xe=d5K%k6| zWFtBLQ0SfxEc_#$N734+3BRcU2W0Qsr1>e${0aGOtDa|ubP&33ugOwqYEio@Pgg>> znws=#uM(LrV>dF#^u?MXEFkh)qg|YsHnoHVUORnLE3@t=>yvtwc7K)BkWRK;{KW!C`A6&g7AT#03>>uy4zHCQa^ltu7T!(JGNh?jWo9wSKcKhDTA5zpJpB7Gz zbYQ@pz0hOdn=!=W4v1Q6wPn5LdfZJr_KmZoZ?J;Lf=LZgPu`;A_%9Et@g<9KkUF9Hegs$iR;kL%ks!Q zbrj35Gr`yS=rgjwrnAdQdFRlREBWjD*WXIe<2AsC6k4eqyDyZ_9ure(t=Rr&aTYSN zwU*8+O#8~?$t!e`+%(r=_*@F7d)k?$d6&8>3Gt8sIAZS|54pnBS4+dO_TzSWFEz!+ zqK6UIJ7?1P%CRcl*67G;ZT`1^X+^=isKz5b>r0!iYAjPhZ_HeHw!$nb43u1DFG{-svg6R)l1&1q&Ywenrn2BC_q6DTx#|2Ou14*- zcjT7E+t1;INME+fBGxvXeDKQ;V6U>DBHif)sS!-!GA$pz$DA~@{zWNfcNkV}@^@8G z)G?y)rh!rSQ*V4Bx3y@muIi>*?-ibyi%Wf1ElkmMrw_e756w|>;|Fw;lIzPA^ZpTE z|E2ryM6ac~VQS^pXAD;Vk2^fB9t-naSt*=VEZW)BE+&r`+QrpLV zsVv^Uee(nHM(sEM>6toIO<8cOOf*Eh2-iDYbkWE3?SO$@P*&rv-D#%JH^cb#$X=u( z@)DeCi5%DCP$bXM)(guj$Lpy&b`^0saepRkD7~W^hpuhwwMK_iw^O<{>AXwNER;LO42GU*i=6SFG5)Rl#Q_K$k5Tm2Ygq6JnlZVFhct=hA~)OY-~%!xe|s z!EVpp+semh@uMZbEQiKgNX?pVL|*poEy&Yr%DD5FmW|0Gkul?^xV3VNu-buRYNj%N z0cRFr8bp_6SjD7ea4%hwUxQfxJQhj4=;R*^?`VZtudfZd=>bd;ZzIfrV>}rDM52IntBE6B(HRF`uqEpgX{ zI2X$N)!Y6tW2e4l{uD%gs~8T#Jn{B_f@} z$~T~2gf4vJA%t+7}xJYD6KSdWFnn@w2Q|tsvi)H)v?aS?a zr*-|WE`=_A-0I##d%Al9UXyY3bB}u+4*h3d2)rk0beMn37tH4#s349a;mEb07K|$7R@NjQD6ASFSzFg z&s&D16ik&7T~ZUCG=inyV3oX3OMW6xyIQ=7E#}@|T(EvVslls|M22;Ud=2^?O#6$Y z3Lp$w2_M!i$idd1SB46mnE&?MNb%$$;je8E;C0J@E_z0a#-c;Q*2hsieGK2}I4sT* zJ{2b|j@%1+g*i_(kgi`9V|WT)u|uK0AtaJZ@7I<$OnYDq_R+tjAB>>#-kcA7{ez$o zlJzu%rgv9qa>}_h^XA?Y9Iulif12t8u zs$*Zeyw|`U8h}9U^`dFMq<$ z*`UxYzY{%1nvuv{yB}#a7FMukQ%$)JC$7HQb33N-y-vm^`Nw`mEJ!iMTZ4=Z&?1hjE7sYzt~Qcu*OJDLna4sTU18OoXgXL3~P=TC9!R=>t2b_lTP1L zGM99s$UUlE%zDBI=lI*-oS|3N9=gTJx81S`txyTjvGduu;W_@iDeu`6sa$7J36115 z9msi=T2cHjl4Wa<^zGF%pGnyrD|ufYU+Sok|GQ2o9xADg^g zn!*)-eU7F?S~dtq6y{HgJ)%u!o+TvRwP0&dSMpTvIqOjJ65BE1x1m*%ERhPT1WiQB zA$Pe!Si!{e(W=WU(3fVsx!Z819PhhsCa8oZwsXxBEBVCSWr>n2rT0|-SKJu$J1<{6 zugw(p4+@28vW`N&K1bl-!Ig2ggFdRTo@zhaY{!#>;M;e11YJF3;^9HN5t~eNYy0XT89kr z5fzcf(zXl3eH+6K%+1%@t$ASRKg1^up^G~(*rY3=&^^o~F-sjfKJU+Nh^vte3tkVE zkPs|`4$bC6OokjUIG=(6YkvDIGrbKg*RifvqFSn{Gx`G@tqdprHx{=gFfMDugQ20J zbH(ivd{R0=5S@Gu?$tK%cD|x){M+)u=^{!wmKn1J$GYiceE%I-)2H}S zpfg~<h-m!#NwJd=zrFhMa3dZ1;{zwX4Qax#ssSzURjsWQ&X;3c#* z5uUcE@lez~?LvK#6ww@#Kzxmb(Q2Aqx6nPJ7C38GVOn3En#xhSDQ?QhedWqu+;Xvf zpk>0?8O1<5DIZx73(r;OiF`vTj(k029oMRf>FUnX#8%5P#8R#!SjreRP z?HC-Rf>@$5{lIS3l$3Oj#Z;Xj_|IPPF_C**>jd!|?N`Ht2X8it#f0Wj8pdTg0C~>E zuTBTk*!^%=;_-^}vpC--@5YErwdn_)6Vtw^bX_UFu1#Y+Hi`FjCmcy92I{j%@+*V; zC4py$p~=G>De?QCZ%*Z#1ji4L5deUmZ~+klU{1?X@b*mpBjeR%In-2_#!W!{J0OcI zi~z};WUce-b))ko)Rpj=za{J|IUbNxLtwmL#pOctR; zhvEAwod#RwIMD9EZO=?-b)56xG*&qMclZapAYCNi`QHO_jM6MUrF+;V<(xtOH{r?4 zNi(DM+NuC;w zuOT!%RuYz}S!bao`zz74M-9#rnq&3e9yadD#a|G{uiMLY`yhS|K?VyycOSUd$C=?Q z3MVq&jC+dgFwn{8GSIKhpDN^gYpB%}9UgiSNs#$4Bmvgzg2coml?bQ(un#UKk_X#A zQ4kbO8UZRio+rnCY+ocWN zDs=oFkGd}8;6r`Dqq7z`KQ?q*`;&xvtT1ef$71u` zk(b?I8zlG|B1#2rcE%XfnC7L#C)l?lgqkv-H1s!6SQvrJVkXvHEa!}fHmf@>yuQ6p z@ppnMk}rpN0Wz}^XgNG%P7XB?$Uxu~mO;Q0scLLpexOyS$=}Xsl&YSpRYXl7zOIJTlN_`z%F5dR>>lK?^@VpCFgxyFCZ0!*c zK;MGSy4x*>GPj@&OPxkgj5OC54BBhXfX!oA5!q5wI#IKy*_uiah1k=S$JFlTTupiN zBb6Wjellfaj_i$x%hw!!hoI*$eM(*Y2}Yl?Psk(biBmA{^aF?Th*L-%ZNDH@z(c zXnYO(FU|3IEWX?X`HfZ@Q~w#d3b%6~-j@oPjV|iih3+kdKFhC%1OL5=!JHdyfjsZ$NsYySp(k$dGcr! z7(Jm)TZKBy^25lC{a`lo99hy>c@?s{Ao=?`?ccGJMGu*~yPbT+-B+H@f6qU`?Pkb4)olb$cN$Ds}po=Lcpea<~f^l**R zU(Rqj$1wg+neCKlvx79U3akb`5Eax4nEaDOP_A6C<|^G_3!f-|G^BLw{Aw%=BU0tJ z-w`@qC(zSSX@Y*Jv`6{sg4%Wt630%5=#f)zr%>f>sPa|tZEE_D)jILcGM?z1^!59g zLKK^%>H(gy_!tjK$_(l60UvM!JJpdqfXDJW6w&=?Ii;(Q6a~(vd>vOhzHE!EcSG)-s+j}| zB`tNQtCJOcnM3NRDwous;@fXUl-nZyhUX)bZP#a>CXvCjtxz?VPNf1~h%E5AU~%Cl zWZ4D69R(68s&k(b+EM(`PbRR(Fm-xbl*GOv7x*GBqX5yp<8pp)EmQ$ZEC=vGiJ=Tn ztq~^3=HK5f1 zuoGwC5i(=nr5yD9{H29E)B79_iJTWd^Wt%T$NEc|OK2#u9G7OQtzg*ag-)K@;r0IV zy_9^ndXwIx*wZ*AV$24{n`LQY!r^t3CVJUJZQ4HvrI=?IHh0*(=Z< zhz--I=-REo1a7rwn*leb_m3n8T=~DhqV$<6Ecc~2@{YJrzXOhl?5fn3pSpH5B0eS~ zKl9S9e~s@i0`IZ)QfWzhd|qRY-eb=ZM}(?V)gSJ(zY$;77iXnZ3imCns4tXf_y4)Q z99UQT7bJ7l?G}8d25^RPEJ>STP<8PgnmCzZtZq9!qogMr6G?{A~O^O6EXb%U$9iz zha!oXtHh{pyn$pxDY-?Z;^c7AZ`M+-qw2M`!p3CFFdgI*-cs$M^T0tFqo1pFHg;Y9 zcN%$bi~Xpsh^GLU#O%!s(Z_~bvBPkwv z#cbLq(r@F&(EH=&%#1z3^`epa#>SD%Fmc7-;l~p+@GKJ?jHK}Wo}m%21_2md*tuUjcOapPAED5rz)8RZkJ(U5 z<6ytg{O6#<@VTXzcTsGM%dbBUTe{c96YC1sb-Kg7u*TVQgn7@8mT`}?|UOGW7A z#A3-W>ts8h87;_y(zTGCQQTK}K3@?aTjw|Kk_}tKZo1&|&@0d+XXoA>{LjqO>Z#$r z)J*6@zAGC{scZcJm6)vq8{`l=_orB_D2i3B-msP7K!lzx2gK^XPP6y*YyM12eZHeW)_vb79rS5-HSTQ;#vxVC=5Ina2)r<>K1vU+jL zFYy;g6-n;rhdC2NpHCR$JmYCSETSCbdkVsB2ARM&5k)?2~miHofO5UvsEmnx##SW*3j#iB!l zocPW+t!NcgzOwZ(tR1ObJ^x1L1NZDI^q=oT@c99`T!uS$;{ZyzA&GAbAMJt_lS$m~ z%vTKy`XrPjj`9%S$xB!CiK!xn>0g25XI|Fe^7L;TW<(`2b}QG)?TbgoS+&p$F7x6+ zrq)=I*Qv?evGszrJ)_^{QI_~sW}}3k0VRE-Lfpv%*U~c-I(k5zseQIYy}|WP+?F?r zKXEJdXg;IxTKe?o793qEFP-32fYH*K(tdzPCo$698p39~_)QjHXS>;QJ~8(RUtdHe zrdri4^DqNVoa@JC!2=3a9>@a~yCXo&2$!;W;SJf8)*@npNo5b z-{G+$_YbSWND3r~wy9b@Q+8ZRWu+Kio?2|;Bu}(J#6>pB4F=HD5>#q{- z#Z4f%KMbsPk?j|=mvaj*LzP(c*3zH$!OCC3a{R%q)4`Y~KISQ%u7P}xEw{^xC&@|a zUBB1l1>SkYcG9x!UOIYfx!))jjS0Pf`39#lhl5n8Tyan&na&^dd!&VKksO9leUfUV zU~b}G!>W>I-F={*fMy`c))8^}nP!B{u>p@9wKyftexU5K;`BD_oG|nF` zeXT4?>*%pc2Ugh-Takf7BQZc|@+~SAw8~m+!w5y(+8Bh2l55YN;_|Zu&=aGvIedlz z2F1{&(9RLdv-6&%0JSHgdMR%Vg-_r16L6C5;m0fTc^@;uHj0~Y(%Ob=dkyWu*Pl^g z*~u`>tL9%uuY<3fu0$el7pz=0dqXf8xO8Mh>{Zb)J9xl=TemFlztf;YsDN7#P{@c;#Iz5TZyr-CB^ zT{|Hw1%7y@M?C!q_i_0RWw=NOXjjm+aVt?gAc;#nVX&YhA|7C>O)O-p zqgq`=9gYYbTQLee;5&EhWT2z*2PpiZcuhj}seIv|N1J`%q~;Y!Q3qspRd{Eivqx4O zA=bS6-{EnA53`gPq~I0nYkIBq?C&w8Q1h}!0d7EXvBIkSFqe-6CJ?%?)a17OZ`4KWQqIo!UN!ty_zE@-rNmNtEj!JAA)`SK`;YfUk_`LlLJTI*I23 zhMA>)tCY>pPE_p@l-_dkI=d9#b@{;N)wnKO9htsoJGL7zdJc2T={M?7RmRbtm9sf! z$9xi<_Xmb28VVQHEJb&SywVMTwiKlT)IgE~Z^LTXp4n#s9beA!-7VFtxHLFbZ5d&#sZr)y@|ZuUzIzh^Dfh8Aggn*0SSHW z2MoZo2U7g18(8*ZFF@sLnMB`==Xi>gmI@bhNyw`b@i|v|U_Kmf7WZhyCaUFIw~dlZ z%$lihI@oE=_fl5{zr9RUL=<)LKO*;x*h~ui<2(R*H~=5liX8e^zM3><2~9`~BvL|C z`GU8p3bQX_Hd>v@d~cCtQjec<3Nt|;I~6YP=-A+SEN`dg#iq(5ZB9vioBO*B28fJhBqzIE%B{9MxgYeVCtsX2X6#pWGRA&<=KO10K+TuCUjT zBylnSH}Zr*ZFP~`ZIT^_4$$0`sE-!ix<5~K|2(Q#UF6=be?bQ)Hr;sgS@$CEB_|n| zqvUryFnMi6K$3*k{s|hpjAfe>Ykp2o_-}&qB;5Fmr_avDH*;#lHYBd|7pA2m)tPJ{ zFnsBO&wh(_u9S{B!{(!(NI3(awQ(=N2amqqPQ#^LYeWY~JeGpwy-BN9OEOabLh!r< zoKqI>bg&3|&ihCBo-w4cTS<4lDe>t0+H7gsl`PLQJ{HPnGS|2{Wr2$z#4NqjLJgoo z$@J9Djn!q>4*J zgQeaLcFJ?FK9xbPf}?7{5M@5T4K$p(4#?f!NYC$n$UsxjJ%o!Z)oX3!&TMMwk~VN% zLCqMGC4(#U9CqjCGczXJ_(dF9lh&b3-+ramnH`5MklKpJzzm{_%Ra3GKxBvKI?^N; z>iadf8$R8h&b`3Y7F*A+Jv?^!ci1Gm9DJtmwtRQN*sr5*DID%^9=7muVyfeGgO>lQETJXIcR6kh%Hobun8F&>h)_ z*LNOoLch_A+LHS&_8n>EmGN>{cBD)`eTJ?6AxZVlIK?<{lMTTQjhWsmk|`m(jnAf| z+j+l^m7pP)pa*_;rPbKd^1y&$t9)J=-It)|*w&pcnm>UuM|!vDeLU0s>+uGAN3PsB7MCI=I@XXV4!JG%t(*K=e0mM0 z0CW1Pbn6beKyzvTl*>;j+ql~K|22orm#+AeiP@j%BADE~n7ESr#r-j%HXO%2Y)3+a zsnMC`L#T#^s8z&1sP*dyYAvmQffBDHc|2D?=plIE(t=0%?04 z{l%b%TF?80+d>F?*#PfM z1B;m3*@hnWNT83qFn{#N53m&QXQ(;3VR@&GE?aj8Pu^$SiA41e;2ik95p13~#vfGH zW<=kq3SJ55Rew2K-@gp5S(rTf`MsFsERd1Ol8(thv&{@QebSIydeT_Y%6F1{plc_j zvHV*l60(luzWs|O2mb5;E~g9CxfzW{Khc4)-X=e~8Hi*EetjG=Y~Fd>y{(xacqO$w zxbV5z>#<ef<(Qx6so*`fGb zY(0qu?Z|^B2At&pmY=(=O!R1R=4axK1*TolpQkpITI?#Y`K1QpbV%q2!Q2$WB#KO4UKPGyfad| zw>`Z!a9346AG=KqSgA`N&M?4tcsJfDoV3}ex9`1QQ^Mr5fsFsFowm6tlZoGsZ2FEq z=U9AJUo>F5v3ClqoQ?+io)MP@EX`v6tVoMi#f_~$IB9?x5Y7E5+nfVLqA!LQ#U^9j zJ9Lmqyfx1Lu#As5BxSC%Vn%FscGrHw^?+)pDQ7rw-a9_`ebe{)pd}O0sJlM8#RHs! z4ubMzDK42Y3b53>#}DZw__plg-G9t2oip*L1SX4{@0@lDY>1znxq?}6dyyEw&5*Z6 z1$O+b!+UpQ^Pxe}aCfKU;%}AU>(>QZ@bQohKR>P&-Py|ROeSa9B>xh$ZxAx?Ge;g{ z0%^UUvCkgYRUe-KDgP$r(En@g$seI=zd!fRK9-s6$}*K*MUghkRf)(Nkt|bbUuZ*! zOZz@5Q7Ka-X`%8;5to)%rKps$WYA*EQi>Qe-^cs;7ryi3%-rYf&w1`S=Q+m$PQ4^w z?IGW2Q5Z+?ruCNd{qC;guJ~3-|0&s9H+gO;t@8<|cqp_!{MG~dQw@9(X53>J7ybS9 z^fe@lr>Am0LU*L2pL14@4dC(8&aUth4cA1gYgKpWnBnindpQgo?ff^S z-$lM@TYv4G(WtcaIqmwXrErQaJey$4jF8}#UrE`*Gt5TkE2%Xd@7BFe>+S4*Qk5J) zyfdoSN;wyvdnEHi(9I8fyKcFARGAqW8LeG=B5lpJSDvF)RbQU$lPN68m8V~_PcPf6 zO5A0%KO;K}n^Euak22ZBJqunr&ls0r;B0ZRKK#1uPI`KazdX@m@{pWfbMw}%A5F9z zcOIocq{63_1^}~B7e`fWD3fw;@Ppf|LjVR`ahcSeBP=HSji`xd0V3Ktf`^KYa@nk| zGF6I+gQ$+yw*of3i4|_i0X~rHF{xF|xW*{p7R!pcfPsq#cq@pnKf<>UTIK}G*+86y z!bfo6F&}H3C~UwiR}+zZ%Z4ClKyA?psZmi66l@(ms3K%wz*a|6Y1$-k3WW>k!Ao#p zF^%fQ)FxuS2;DpQ`h)hesQZbzTyexWk`9(kJ2C{%@x$BA-~tpPY)r7tBPxRP_<$W@ z#SHl37xH&H&_S>ivK87h=({Ep+we*kfEf(F6C|kT439Ps|L%PT%h01*Jy5$lTQkP!!PEV{sp*y9bG-%yR$sE0wLK+Yp5R}sU7Qj|z%W8L=U6+V3769zV zU1nk96I8u=%wPgKr+|SmxplO`IB|zQ;*toF;0m5^Rm3;oXJiJQ1fZv)=zN4aQAaD_ zpj)F*IR$HVhrtnt&vXF>hup#4mbLyH`H)K$0wOp`mADtmkT4S$15~IIZQ=C$xQhyf zcqmv7GWVKx_r5Sy!HQ2o=vX&{Q8UD)49UWcZNbDI-AXQ^AqXEbg%N{jbu;o5dgc*5 z!V~}GT)txk?%R|?nVYXb(pt}{F}Of31HXD&P%x5iE4?O~HpCSx+fmVcF9*ea0AU>A zH6<(JGQDqB;umj)G4MX{cp7Gax1Yoi8!j6XcV(NE#?ZtQ-}cpxfr&NrZx@V-+_+6kjSC*>Bm$lm%Id5Ah(%f!yJl1 z_L!iir2KR65co=X>Kh$V?--fMhJ@~KI1X{pXGf?2Da2|!bVPxpFR(S%%>O}LsVI*4 z7tGO*J25r)VFQ;l;Hm;tv;jF+hi>+BHl~QAdWB)RBnsqMA%KO0 z(3!)s9RCpz8AwHEFogaIwKTj5AC@JdY!4h;npG1?Dz9 z04bXf!^@x-@A{`V23GRN?BySp@B;KqgQ#(2FE*_A-_~^whNnUqgWp5D!UCidE$#vf z(y@JPNdpCOe*^h^nuKDZt)v}oWA5NIi`5;4vHErR91u>4LAN*h$_2h-oNdAT4knuW z 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 literal 0 HcmV?d00001 diff --git a/projects.json b/projects.json index 28785cc8..d81c1672 100644 --- a/projects.json +++ b/projects.json @@ -88,8 +88,8 @@ "text": "Eternl", "link": "https://eternl.io/", "logo": { - "dark": "https://eternl.io/images/eternl-512.png", - "light": "https://eternl.io/images/eternl-512.png" + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/eternl-512.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/eternl-512.png" } }, { @@ -120,8 +120,8 @@ "text": "TosiDrop", "link": "https://tosidrop.io", "logo": { - "dark": "https://www.tosidrop.io/img/umbrella_blue.png", - "light": "https://www.tosidrop.io/img/umbrella_blue.png" + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/umbrella_blue.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/umbrella_blue.png" } }, { From ad8342650502dbf7f5902750a0467dd46e406cad Mon Sep 17 00:00:00 2001 From: Alexandru Dochioiu <38853913+AlexDochioiu@users.noreply.github.com> Date: Thu, 20 Jul 2023 10:18:11 +0100 Subject: [PATCH 72/86] Added vespr to projects (#210) --- images/projects/vespr_dark_mode.webp | Bin 0 -> 528 bytes images/projects/vespr_light_mode.webp | Bin 0 -> 8198 bytes projects.json | 16 ++++++++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 images/projects/vespr_dark_mode.webp create mode 100644 images/projects/vespr_light_mode.webp diff --git a/images/projects/vespr_dark_mode.webp b/images/projects/vespr_dark_mode.webp new file mode 100644 index 0000000000000000000000000000000000000000..4642d8ce01a272251f1eaef76c9f9a33ad4efdf1 GIT binary patch literal 528 zcmWIYbaUfiVqge&bqWXzuu%BJ$iQG=#W0sqi-C!O$<9!s$7PY?LID;A1!k5l+Y5GY z=v~0_jq&|9Kj-^7bK3XawB)bdR3!I1<%#XLlP9X*PM+}nwz}e|^q>ruYYYtk{(o;| Gzy|8fCNAav*Sld6j4NdI-)}a>*)5u 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 literal 0 HcmV?d00001 diff --git a/projects.json b/projects.json index d81c1672..477cc4c4 100644 --- a/projects.json +++ b/projects.json @@ -72,8 +72,8 @@ "text": "CNTools/gLiveview", "link": "https://cardano-community.github.io/guild-operators/Scripts/cntools/", "logo": { - "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/guild.webp", - "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/guild.webp" + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/guild.webp", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/guild.webp" } }, { @@ -88,8 +88,8 @@ "text": "Eternl", "link": "https://eternl.io/", "logo": { - "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/eternl-512.png", - "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/eternl-512.png" + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/eternl-512.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/eternl-512.png" } }, { @@ -131,6 +131,14 @@ "dark": "https://www.quixote.systems/wp-content/uploads/2022/06/raw_explorer_logo.png", "light": "https://www.quixote.systems/wp-content/uploads/2022/06/raw_explorer_logo.png" } + }, + { + "text": "VESPR", + "link": "https://vespr.xyz", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_dark_mode.webp", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_light_mode.webp" + } } ] } From e65ce979e62053056a72e24ab89cbac5b26edd57 Mon Sep 17 00:00:00 2001 From: PatrickTobler <129864078+PatrickTobler@users.noreply.github.com> Date: Thu, 20 Jul 2023 13:20:57 +0200 Subject: [PATCH 73/86] Added NMKR to projects.json (#211) * Add files via upload * Added NMKR to projects.json --- images/projects/nmkr_logo.png | Bin 0 -> 20498 bytes projects.json | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 images/projects/nmkr_logo.png diff --git a/images/projects/nmkr_logo.png b/images/projects/nmkr_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..e9e47a7533365f849578237893ea08af7823407d GIT binary patch literal 20498 zcmYIvbzIcX^YBp;BA}Fr2&lA(g0zHm_t7CJ(jef`jkMC;al{?n9io7A9$gY5UDEaJ z`T71{&mZ@Kt=ZYBot^hgh>DW*9fAi05D4Ustc-*j1cC_%zhJyupeK*{&kFd1|4~NA z5dyhQivD3hl2h-4P7FsiX)#F2z{7RWz&3lM_yz(gkGOqlj01rv=gUgG(Qv`op2JPm zSVeX1JDNG!vuxlM1P7?94oesavi7_Ud5iT-wH)GP?bCdF!-`k*9(Os$%aG&((V|K1 zB5j>l52!h{*NO19+P7ve=GS&gp4B}&nXl(^?A^UM+uK2M{f2FN54aAvzQXV!k$FlQ zGOb|qSnHAUeJQ0YS!Ao-{b-Wmd*zAcSy@*2o8OYp-K9)WA_)>)H$8BdfuD&t%SqhV zAEZX%-3wed^~TrDiBZEaoAM*)S5FLXcx;>53u8V>W zocHu}ezdq=+{hNoHNG#4AdZm@dB(Pqw<%s6ulfs$qjRK*qkX8HXESr~>KD0B#$6eV zScP{`F6s$3Xx*0%co@>;fr*CPihtfjGPahuJoV$s{*yv(_IQhllI-L*g3MSvID|k6 z%1B*j$#&2f3I;hMgMq z(%-TgS+LR27v8ai!iGW&A4g(-RH&X#cwSsQcBF3_6Ka-AXQGJ%J;eDrrd{+pEv>ak z7t%(L$>Kv%Od-r&%mh`g`VvXd#t(W>v39dHoC$hKmIWSppZ7wVP(fqoJ`W1B=ZN1b zVyn@$YeGei(vv^HHwrU!#eCW;kp;i?xZpL&yifuOYG1No5^_=RYggAE!i*1_24jYU zv5@Ob#>;*cONGaon8V_9IATTRn5@y8@({_k%X=!lz01&QN6S%6x+*-l=+8@sTN*Jf zRf~5YTOz#U&W>JNTpi75*xw?@qr_w(`FLb=YI^l!$n8P)$rGQ)1R8wYY}YYM`z&cT+brDFr{K%OVQzY1dXo-J!{N-US-VYgqyaU*_~#Q8?!KS5bmj?@*ZTp7(8BA!lma9*SWR?*(Yh zANi7*`yFOM2>Ox-)>2u@1Tb0XQi*X?K4Gh8;$yNr{z^xX`D5gF>5_f1Zbzr%0GX;3 zo#fNI5*Vin$!|lMpF}=Z!^?(ThIe~N-1Q}k5+%VQd4;Vmf$vL}B?bQfXrX%+jETu2 z7D7ir_kILh{WBF95d$L*MPXvhcV+Gpkf*UmPZg0%;Nn9*(|!Ykbds6H4NIf<=^-RS zA6}pW*CCm;)Py;#$PxW6bT@UltrOp zDq!k;!e{N#n7v`SBJyDq0*uG>f`O}9YH%-=LSn`lXJbz_8LQED9_VE_fom5gB z$O)fOC>>p7dxD0$;e_M60LN^|Vu-eaR6<-w7m30zX?qM0iEUEu@tE>J#;v5%u_sawbNVMt`KIT>C-)GRyZ?1Dm{U~#_wz=0@n-LxVE;srpGDIXFK&8k{EO(psN6MF(b1fB-$GCs;{us$gshw z@a`aUnfWkTUQ0vCAYwt5ECivV1cL?5*;6iaIUh)9q|n_VpxaNBK)GNh-qK3zwcV}Z z9ZU!wSE23$J8I=WDSvilq%3iMlJauXbIjIYL!#nsQLOWYPD(bqKCe~i1FTl56ST(1 zKjbIw9#cR>$r$=5u3r?xlJ0ZYBQWbE0?0}VzA@Z=_xX$=mINZYVv(#7m(Yc)&K^Lm zA@d8*UmoI9EyKBoU5f`V`}&Om(k7sdrChc5i;UfUQf(nSVHgwQBSiT68xJeoT-7s7 z*i2Ir^;I4MnT*d2Aa2BiYZo%A|BVBa9IN(>Z?n6<3ZoE$w7o-pxZNtk?(vF=QIr%Z zNxP{ctcjNcf!IMy^pjGu8|*NPeB*_@REH6d>pjjwYx|wm0f>`(f%I!5?On+Lmx4GY z7!0N^QKf_l5jDcBuv=8$w1|nDzD-p~dH70S>wQ zIFMM0XvbU?^C@YK@2;xVMpd;Ch>Ace8;;2PTrha-?MjYuCZRnkn0^rcAcvp@(cvjGX zdyO8>xDK0h`6BBE5!FuIzSt?}F^%iYtMAt5eITqb6{Jw)d8TsMZ;k<>OGWBchn|>W zis&a+5J#O?D}7dlh!#MRXL#I9^munz;q=CuQqoCwf%p)}a9ha;NtW1ykA+TO?nz-l zqOy6q70k3O_*CfcydalKc&3v#F0kK^2MI=g$+Pfxysf1~hE1Vs^>vi;p93Da5=;CWAnRG(uG#C}593=)Yh>%9K8j zK)@I_005c6%f%3q3jc%2$;K5Nx%K$6)ay zl^A01u8e2!3j&DG8eXoj6x|UOdNGbT^AQUsB7y`QgPBhZK@2AOfk~pnByygCq8nIf zsG;F18&9xD?mYpJR5(8{vm03lj#&6bKaPx7d7LEk%&29_^>7aG8zR^ChsKIjnVS2Y z@J+Y5U$lzxr|xJ|B&RM51d<6iDR(Jtz^R7%MF-={nRE~KO5vjqV>EO$`}yE&?^(0{ zW3WqbH3uz2AmKd-u#O@6N_hoSL~n-g%7i@|Z`rza*#80pn1~n^|IW`NOUr~7cTqvA zHlKkOHpP8#?xAm!JdRK>B5D;3~eUrjm2ND;MTptt^>IB)i$rL9A1>=ulDj{UI2u_=2!F11xy)^T+ZCo1lmu``$x$ncJep&S`uC z+#*+t=m18Jd*KTO1VSQJyt@*>a`F15DltU#{Y%%~ zyLnrOB)db%4CDKx;P4Xhd9qMjz z89;VTvpf8<(P=A6FUMAV8bZ1+#ZsPwZmTSd=^wN0aaTAs!xgn5DT#ncigSF*8-(O) zDN8gx`H98nv0&aLNmz$F3hgEpk($ZanDg-_1I9yAJ_+&0AHt7S=naA@!pT_YT#(U+@ctar)<6dN z__?<&z#{?r@|RkJNwe$$rUmIf(W-wR?{`4h%oq2jtZj?la#w9pze=Ts8qrm4U_eCa z8LG!klcyR=?`dmajQNl&gPDm-_EYR=*~m04MPGFcKs0PG;GL|q5V61zK$ItHz(qWJb<>Yw$FXFCBZ z9*+--FMif+S)j0h8iY7(3P-cMb3-}Ms=N3^)pkz0#B@+Y2%H-6vu5WBrOel(D*O-k zw}CK2gx}pC5t^?-X?VIC=std`26oVDJ_}I-%=S@ED!x#?_c96*>7WHx;{_|JHf{=Y zl8;#PbPWL5BqW_GP=%P_Uu&XNk40%*TugtQuL@% z&C!zo8PFoubCd}LzMzk2w}5$z@eV8?cJmE5P#aXjnAf5J3!(amnk~7^5!eN6O){sL zz#3D#JDe6$B7kdneBaAzZ~`>V+2Dkn7!V&4SHFB3}ZJfLLxI2Xxu2Rph2@?J5j+{;#LuOBRxk34`(v zfrU8iQ!3zxgnB>;H9oi&o?=k^Az=9j24;!tuG1A7i>>|kBr+cMOn$wAM-t3^tTYN| zt$i^gkI7ocei2$nkn;~664}m~nq(wLsk}S{r^0mbo(EtIic0EZ1m`W#B+mj%TPeoa zB(b#Ua|OUrhng}qW&f{H=Jcj0`W8}54hA@oX^L)G_83~xWY8&$1M#YQN@YcwVFR4U zdnCSyNHrSp_%7>u@l*vm(uO^f>G>A0NsgHcK9hoZR|XLdsKZAJuY~(1R+v>h0tTc$ zQ(^X^MBc4GaGwbb?0u%M zCztwq?ROf|5X1JP$6{c?-=|-I{)Z`=qwrE80n5Z^0Fo?ur%_R=r9mLp zN(>N!V9;(NU`(?b<(USS&vCS?X+R$}%rvAa(LBLI<%4`$b~i#!$W#$qW<2OVJXpFNBF)g01(0|8^7|Ru}9908FN-}fxiS% zQJy%8jg^tbwBSjR2i5}O@#8)#gIctxWqd$V2`(Udf&^mvSdtiycAW|SL1}q+KUb$) z=dUPoQ?0_`1z0pbuw|kZZ^C3TjK$I@4ZwO7J{6P=dmu9<1Q@(%mpdt|yFy@51eezW zgF~E!DF!Pwp8BB-^!SQlHys|O=<_Y=O@~&HqxCNoZcaYp(xhL#8RjJeZd%BIdGfG& zXhAT$`&TDmibNp=HJ)dkHf*^vp`ugX(-8u;}JZPu|QA7hT4NOn*VQR0sJXzwA$N%JMgMa zfG#FG#VD?& zsP1_AGlmViXP_5TarCdBQ}b;K8JNMlL_lUb?7k9bQ~H_X0P877FCbHN5AT`Ev$5ui z65Nwv6VSOpMuQXis0@A8|JJa8gAb(rIRgNMM=Va@WTl>^dn{T10j)cdG0dCVQPG&p zGIXYMXyH5k)1VsZ2v5fy3521=9xq9(*!$phWW^48+f)LWl{&rdF;43)*c6hh8hoUj zehWPnXGRq$pU(*Ek8>tvES62QSc(noT^~Ft)jR%2hj#Y$BYw628*AsdcC630aUaCA zIO|;9*fIwZKnRItV3zAN`N^}$4S|26GW-_)L;O+AR<++VIW+j)rwqn6S_hxae8*l8 zvckcVyLThpYYn<%M0?JFt@SbAKX`~>@{K*r0b{DmbNp(hy2o3zU~MqP;Bl1OJ()ZH z1T5g3V?VrYD!j)} zc5Ph^ePaXA%C4Gl1NQ`te^R)z+Ds=>D%v9o=nm=d5d-jotng2+2t+!FqgOpx;YK$&hrq4oW3*!bPW=(Nnx-(2x=cf1 zUY>daI66+zeg_T49A2Nn;Xp34xv_y34#=yaA6NEDTD}?+-2be~sibfhzX6V#;;0*1 zGRow{?v0l#b?FmvQ>CH_*k-w8KL?E)gVLF{0~-#p;v{5edCo@!zoQkXMvtDbp|3P8 zNCXdj(}*5#{p~a|Z~wo^{Vxe+LvPy-L^6VjkW+3gXY|ZjW1veu)eiRg&0$<{!dGa# zUH4Dry1E4CsUFMGm?L?LJkUsw=Y{csR>c9@*06IC%v6}Iq6smcwWxlK zwpfty=PX401n}fMeqr>0Pq3`U)3n#K#sRThXn8T$d0iqX@O%AZK}Z~0zO^aMBo*?_ zWDsZ($dpuaj*6f1g2O22nDAPiY-VIlzJHGG*!Fh)taCn76)Tpn$Qu``S=@ney}j0b zu-e;UH+y;Fa#XHmG#GNH$z4e3qH)UdPWtIwAhk6!wM|`=i}CtFZpGVQnB7`BI!Q-o z<`a2o+iPPbMNxfA9cp@dY#qS$eIGWEBCik@rpKmP5=pyuZGN$DEBrh_Gog^{M$j_1 z}HFt%>s@yQl0Fv^spJqzwp{XfM)?fnr*90uZX0^NZN-tWE0q5~PQ89dSj3du| z_q_72P*Z8n8p7zl)||`N*~7)==>88K9An;tBDANCe`j0sbJkps%6FYlhYyA_Xvs>g z8)uHAuNm3B#`6@Z&qit9y>GK;LilQQPi(*2iKX7HW7$=iEccXloxIe4=(M+MEy8rw zZ_U5l&Xv3I6`^Ln@eT#sv{{Y1=;}?6a}TB7!1?7#d;_(ugMB7n*-{Dc5tN!|#8MVe zyfhhT;^(yc=5e;uxcff-?C5&6kE3X?L#y&bPwj#?n|#7^^0zj96Iv-NAmml zaG#87OO)IIl}C+&`;AS~@qD9S`yX!!BP!U!{ryUs23ron-Kt6^<5zOiH5P1Igsoyu zQx=r=PZD~&M`JRghJQ~4&Ar&rO5<67e>K%{DWImIwV2Vq(X=Ted5m zxi-8`5}ED=-D$oUtnfa$DvqEXlE?~7);!+XOZ7lmvKH3e* zi1S1kw-F@combyA_pxgI@rV_C#z|27hA>hLM=gE{>zl*U%hm+aK9T*pi*JOlN>%8^fCj~hM-kCM)C zQ7G#PX0t0B&~>zaI=!@SJ+kvI?crY;0-rWj@{z(oBYNthR^K*3OFK9c^UNmH^JeFw z`*#YAq9{ez-udF^3_R%UrK9+^X)|n$#$i|7`)-kKA$v`%$ikU4KXNe2+Aa9i_X`5V zM|n~bnT^zC1XH@nl?gM4GjEaaZ<^V|w^I%BYL{Z$kDzAs6Lz&NnWh`=zh=L!$t(y< zhv|`Kq&+1LBvQoq7?mPA^O9OJ9bBieO4i&3G&8*yF z8Zb&d#<(<$UgcAHY43N1j`JRy`X?7?3A)d<$hg0o)^j+?knUb$2*a_OFztQrFm7Sy zkp2q!>VjNNFKoZJ*D>T_Z6Ky^&CEcYLobZC*FyXj8A|s59iy$+oas5y*XzR zLr9O=$Ovwi#V8o;CC5%jeHbzchMT(Ywj}6#cN~r5+_UvaOJ@)-ZPA|ha!x+|c9vlH zmWF0;BtFH?Vw1rY9~o=yGI`fnR!RadG(v@A5!2~JGw<)B7RKT%?vHC;6N!C}!0m*8>N z42wHZhhHN$jkXS`4lPde#i*VI&E(>X1vx~SdzC5D&2vgJK_)|-$kDTSHO;$8k5QOQ z*5U4>0vE&vdA-95s^a6YnEktm9wBUP>Xe0~2fk!@NipI4-*j<0P}%b;{?xe@n*)tM z!v#<6WLu3MPH(F4?|z{lpWC!m@H&#LKgqg)=haAMTd(%SjoWA}(|StGPG)T$H{pe+ z!{>0P3E`>qIPD)|zVb%tm9!?g=LZt3TbR5K_u`Kz{kgVkG$lm+)S_|Hx;#((f_G#~A}lJypx z8FnO+rFmvhJFgjv*!?9f-{^GA=401#nAewjRsA^$=y-zYR!gsK$$-pYnuyl(CX<58 z6Yq?=WabFJsRgg)I)|cLJ#{U7p-VNK@J4%aNXy?d${i$s$Q@G7%O>NB_0BDPYxn>m z5@E?Sb~jjq*x4#zR;BpQcW>vq?XE(r=P087fG`SKZA3)(dOl%%i*#LMjv=&T_r7EP znZ28QnNw0n7(?aLYqSepA`G*OT}vjy?}I1T*-Xy7PyB<8aw&6r+96HXv4%0J}}g$eW=y z1x15$_#{`oLiPGBN$aZz9w%4MAAAZO4#)}>i%Lk@UmfuajJ$zqxUO9}uN;=P zyE7g{j?s^kj7s6JwN{MD-+Kbq!bGib+7FGQ8Jus4gPx zmjVg+vHA&&O0m>SD1(DSEEif{t(r=ISL_MtN~H^k--?cHKA!9yc1%yYEE)@_5@&pO^}dyp)F|3MX%+mVG83WI zD4pSTO@*IwG_I$tB;p#W?=o;!GLG+gFqMz?aJ2n>%9IV-hdfK0O-X~WBIV;RpB?@l zB$fCIzJX`&gvcs42rDB?;7xzq<#VM^mKKCpcy~SaO8T7YVl>#hIW%Sr0}PmIt$^Jy z3bH;am`*AzX>LCClZSh@pVbd{gi-eXoUw~FV-jKdC$epE~q&5G<`oc%q#38A(m9|N9;Zd?JgI#exxjbqa}I`4;-<=}^#Cl^y*P3I2^ z7K9p9+Tp$Ndfl)D12&-LQmJZYRCd&kL6>W1`8We&AsRp$To zez2B&t98(8#Iz8;-E|XtCOiKE%OWvv6D3Jh8&L;6p0y#i&oOBxVBuh^Z5P;5H;7rj zcw}bqda*NzOE^~lIg_ZP^(?8j@h!C$pKOV=ec)>gj;DP`K8PWP9i>m>7Z9}xHY+zE zlGc;P4;!tA8A@}P*X=%}d|NWASXt}1$H1ZFD!b!;DqO#@R0uCVlkdPMFL*9+B6jGw zSAFTeL>hG!aL}P;^mAsnnO*vk)93BAt%}isicum#-344j6|)oIAAFn|deeAa$stlX zAQu=Qfc$ve#?>G3{ceeOUP0;l?LlFq>-}BtWD(L4KbX4NC+K||d(=_MPL3AGj**LN z1;pxm2kJDAc5R@1$NtgHAD4O{34H`pz9qY~*PC@0t8>Kjaq3>)6{z3#g4I{YjiW10 zl^x@2k-`r1No#Trz3{sI?URL{{@(7)4y~WZIQ(0~Jw+RKQFE%9mSe^_L=GK8y{o-R zMm@Nw0mgyvV#S3BH&5h4kHt454b@j+Sm8qF_Xjc8YxL~cE}Rz@hQlgah0ae@pqQwM zT~kT}*5V#LS>dti>_*!L@mp!m0x=7lnT8q$CQh6mTu z&@*W;SSBoNJ#$WOZM(?uD1tG$jTJ0E8f-aDC4BfXd8=vE&PJs4!tg?yBXeXSkHJXE zIl}93t~P60sG;=CZ{vE{+y1X#CW9qxoZo*iTIA{|y={H~E=;k3=TA9yQs3|}2ewHd zYaRK*2x9ZZ{}y31cw7{1H0gw@12v5xvRH*I}pqI2&>>w9uvNFRYrwh!Qzpc_Qum ziI?kgw`y4_9a}9Q*dh0Iew~Kb4^gc&eY^HBtavSB4OQuACBgkyu3HH2?h_FYjyIn= zyYqXq5aK^>Zm|zGkj0?B%n)RSw8G(szX9apDQ zN@M8MCN9sDrp+_MWv}*@BQUEzS1Xq(LixQ@FLGWVoGz^Td{-aYEbuQ5_+|MuC`PO0 zt2E{qYjO+LdG9=~bAq1qoGmmX)ewg8nnEr^Zk$ipM_?y3E8vmbtGC-iGkwv}9I2BU z73DnpY**RaXja8vAjkH~9~u{)op0nK)pCB$25tgreI>r%CnH(ShXzTCX+t+BBjwFk z#`jk7@z9;OUGen?{GzA@TLoTTMK6>LgcFSOPvjz@FAznj_aOq_cMFv%AwSH#}(p} zaLyYcf72h^Pl_=fdg1Zdt&ik7%(3%on-R_xfQN@ez0Tagn!Zk?(DBN0_@maisq3xb z=Ab$hc*yZL45|X>u|;IT%`VyF><&+ipS068E+wT=$}aaLW8e zLz@b|Z=fH8W`>`_{|eQ&Hl1#SX&#MQkZ6cB&AXW;>vJDiu6xejJ=g=%Izb~)oQTxC zo>(tkJsx)MG448!O4(`B`QUx=b!==LPxGW_Ol!3z=z8h=nfc9=^W@?I$H(;<8c zqzm;ngpYMBPr{t-<#6EXLT+-DxTwY(&wY_YoOwOWi?OoFAxDmifzI#E$;l56 zab`vxhRMILq}a{D-jXO1a(`1tPV?+f-TZ8%ZQ+6!(3!*aiNvW^`OPBZo(mFRO;WU?wS*a7*ZVto12!EM334#r(t|t(ZrB z?9*8_aOIfNU;1Q_KJPQ)Guoj&$^3G$##bnV!S^PBEM1*mrC7yvV;z6IO;vb|0|#p3 z>2^>VO-%j~5b8T&ZOrML`dhGpkALF67t2+tBT=q08*l6Xq~0#F!pRYXE~27>>K)gn zy}}7%agSb4x~Z=GAewAcHz-@ z`n%c$Ux_F;U!Gq{Ar4!J*4n_t7Nx;^nITh7k0j_MzZJaQ=%{KuKXE}>YTY~AQ|-GM<@HP#*n-J!BH&>+cWSp|$q^7y;n6`o#s@jMRwY7}rAP;HK=%In)!i_@z< zdn`9Lh*!O$*ln#F_VXO}&%PI@7-X+x++649bQQ+$ zwYcwsm&sK)-}Lc2)R_>h-}b&!guqL?Z?EhBp7$14Hizo+zS@`{F5!4G$yi^VYTvW& z_ZuBLvN91nG%A_eWPzLaYoABEhc=S z`TCgppren2MrbYZ$;C|i%C+asvgu(z4`t(rfn*B<4-y8;!cOC^zfraGuI|-4LCHIh zV1xGy)3#!fe~msKpM8K$Un7gvRKwO#-Z?WOwtnypcwdT$vT(t*&!*@vs=#E(<2B{T+e*t zE`yzdm8)E9QISIU>n%KclTIeudbc-sH7p}`Oec)!fm7Z3;pU7qCOuXoB7Nj!FUNp} zseY?HY;{>#B>FFJO?X?}5AldE)jYv<)sk!dt4&-%GWm)EQ(occQy=wPmseI19KA(T z78p&UbU1srp=U0@fEA@B_$yPbY$Tj2YDH8b8I?fJTNF}B=w{?S|8c=PbX=w;!?_cY z-#fFbXti(s_fecXxbG%2$h1)3>?5w(IcsA*k5|_?w~%3`8`SC(=k}>wcHy%wU5W=O zG|rqxuO=6kr_DiNu2*tRpBaRnrjJ_Y#R}bWdZux<0nNybPlUFv&I~xxnzB%nHN44P}o6-xS&R2P?f4)G6=CT);m4)M}XuVhd za0c(V!xGYJ7VL{}^qWck)GuH}f7*Mgv3;(U-@>YzIey!~ZQB$b(Ry!MvzmDtb#`_( zYP4%lldJ@;S{S~BHX`$?6#vG_V_wr>+3k3d%M?Tk$ub#|^GIbd>T+1+C+0sDZyp+3 zCVQMlX5GWtc$b?OmZmXOv(*%!<2hpowk-};L-8{;O4E8CybQstuGt-c~%@ti#nW zo>eQ%RN_0v=3hO*5^Eh)t?pqGI-xDJG)s7JDex2)%ne&e?dO^68{QelxlQ?#O+8;` z67l4ocb!@6<*)hS@bTLS$~(@N`_HW)Jzp0-U3^Uy+Edunx4Vy^RCoST@0f=A{hRRh z`m3p?%ByA8wrP;y`%1o=Z`XZ~RkO#g0c6(l&-uH~jvpm6Qj zsc->!vZbwC0#?cvlR_`9F{C53phHi*oLwq!wqloYgAG^f@MN{s09PfI+dx2t>SKE2qZRM;3gos@0a0*6ld%m)38WE(rOb!( zGrTPoxf+^Pd*{^YXm~%>V#3FWy){QW}LSLl7`RQlxM%{RkJ-9l}HmBsL#05)0 z&Kxm+?Z7F%iP{PD&L~mYWER)feji^c&CCvcFU=-|-~gJ?XvJ(9H8Znbzv* z-6Hn8avvex^8JMVymGL(@aTLsSv7x5pz?5lPkOql<|dn7#xUtT6owU}&iaMup=cc0 z4k%WbJU~rwt;RYk-9FOk@F)<1b7(a`HtJ7Z2VoC5Q8e%fW(4Lxws{G?pGb2qznZmc zx#&dE$;BcDW2Vd9yQ>o#2o6|-i5|R=Q4-ksp<)}Uf*J;{<<^GwcUQAiHry?DA4p;M zSl;&QK3h7)+de;2_&2SnE>8AK^iGUVmS<^KbCC%Th0MYR^kgD-E>hm)SO49JiU!g4 zDX*Ost+XpU<2cIxh`CXYy4!~!qUNx(zV@?|h9~~T`HFYO&lN30viva{QzjZ`znoIz z%&2}*%d-2lKEMmx!Ov$Mh8L)}FH6z|&I|?NdsfyTMLyRA;XffWNRYeV_ z?4FC9T^0^@e4TSDsR|rvuiKG~Q-ocit$FZW$GtZ-Vo6k8K-p>gS(EA2dQ4fc<_tsfZn=5co& ztKPRpO-mz71~kmPzW+%(Stx(5_tCA$I3}FBw`1)NWu|YfFBv2mdF>MTrRhpadcsty z`<=4T&YykD#mz73w`RPW2=I3YT^8(dZWpT4b}=yuF=Tk31=MBfj+vzjOj`a5`ihJe z7XD?gMS6k`S{+BXFTIaw?k7M*Q9#U>(BZ1aT`C57FT=;$2B=Nw!GiM5<{y3~F~tud zR21o#l%q$FRK(q$?ACQpt8T;?@QJG9QNTXmki>&_A`2?34hXMOirtzk@B z!}j|CiqifUGe@+Z=jCnG9Llabk6gEQ9llMi>6y8usr=epIt=PPE22XVGfR_OTX$V$ng97y3;%F>OCl0E6zTotxn6A#uwL2%8An1 zgf!f^{q=l@2e(qO8K_-IV`O^dDE0;^L7I`oJY}u9cn22vH>&IAx%*Ce{=+{eHc`{x zT)S~7dn4rsi059$6C*{ckZ@08k?G+>o|&qcv(%ivUH@8x;z%(W^G=gS;%Z766cOV1 z*Gz5TZ^VoVkG8kJN_v=I?HDZ1lIpUW*N8*Q$Rjt0IuZ4rm>84G?{9^O642Qp zLmjTzd4K+m&=DUS^N)DFwZ0fNErj%IP3bl(N!;pOTshEDB>M zC+yyJDAer%f1)i0@QnvaXNI_ojZ+XQKsAR;B4o+2o;zXi;f`QddPDtszw`XPQt+{4 z4|u)NGg1rmz408B`5J!&HmkT+-*SBp_=-GkJ^6L96#F1r$azm^q<-{+-=E>C#1oKw zQZ-xS*EJ9SjyEhIxdpy*%@wEhl--Iz1~ezFLj=8k0;z7Xb=UT<{7Lz zFIOWfnmS!o7R&}cR~FN8)fv9h9ZDMkxuNlZee?tsF^xyWwY^+18RTQ`SKAY&C0Y;9a)fM0=VlP*{iGoBXzaXBe&gM{bojPBR)urb%ZxdagY{{WB z8B=LeR5C1q<3PsWr14&S7s^>_+-;={zC5W^UirR3eE8@z8GNc}I{ek>x1=KdL@I^+ z4X;(|bhQ;z0&%l~*UUEWJw|l6b07c40Ty`Hc10A4ev^@~gcBqw4Mc);kGH*5Cks)H z*B%L(Ez`#is9x6vbME!}{CZ^d&#?#0yu9Qrmb~lz6LZ#z$wA$fF&Qx&AG{cB(Sa#g z9$8eB)4^L@-`GuYjL96NZ^Y|s;`)V5b=&D4V`+;a z!)~%sj1nRusA1m%TnBjVgWc6xq5fR(>Z&aazJn>s3?1^kv)gO(V^<)Wxg@2i_!WE~ zWKU{+>zjExMv|2LT-iH?f>-0d(Dn~?28FTxm{y@<-U-r^6z6$;68!KQE(@hc4?&7) zuSX0S1SfYq=hM#E6q1=QOB3_V-okCdra?|;)-`1Zgg;3+tjoUL8gU&R2v5(s#rK;d zaaRYXrD)_lRS{OjYPb5wm6hb`#R;y?jtR%_>DYtitm*0vkKCBjZ7~nR)M@xKW67XM ziFSRv^~Ip&{>u$_uhFl()wUiEOO3RxY~4FXFrJ#x&NThBsH4q67MEV@DdGW8t5tdB zV&6sxAMNJ_oRqU1)7)P0#Ui$6cF1&;?;HC=8?gP=I*UyARTC%7~Io!*}?d z+#MwY?R=kjAOln^r{vZl?WT9frnPcd*<=5gJ#<_|MVLq zpO82b2rS6Z37o2Mo>g^zD8sFoV}ni*`e32kx39_Ag--hFGSx;y5I1O?Zr73thzIF> zkmQ7yF($v*;6+{lSy9K?R9fgmTs2K}u5c!QU~r+;@3h*jO)U}o0Fa~wQq_NqEcvBy z!R7v8%4qVkn2-I!ExFJEood(K+yA+Fi8Jy+>JxyBsCo(G7P*#htgJg2d$sD+ZfH*~ zlT-y#^+eTenYPHC+1(kp!9GK=z4-1O+p(U8raEBe=dc-k_s#mHXk)yXW6&S*#r#Se>J4G_A&2u&Xnyw zA!M-uxclINR`_0`Q@Yx*i7{g2s0vH!AAG>qAXqi z>e7Eb`_@PKyzCd}pZfgJ`NzRSulBco=1H5$klRFq!_eJp+KP0)FLTG%8f+1a&AyE~ zQ|+h(nW6h3=sofFoHFMc30T%-!0{e`>pjc~4Pti>a71Tv8Lu4$GJtf#3-p*+=`?yU z-#6A1J(jh0M%P#i_N_`qQ}A_gc#njoKF zDI)s}>(=z*UK7JXHPnVa#wIA~fQWUzFu_^>?7mPjDATDhI6Dn8?MOHq3^f^WRKPcy z4{pC7vH&jD6>N$nki-9IM~e1vXQQ!Guk$ck#zj zv;r`PZUp)s`LUP-Qdev|{Ex?Nq2yHtc2^uS^!8RD_f0gDe8VX(kH8~vOl?o&4j`u( z^X#^QBKhk`o+`A^lDtY*jm0E9j)y03NB(0vB?% zN*lZTx^Vzj+r%dWwvFillbS7tM-ySmH2{ajFWe{Ui;bHT8e^imwV5Sy<}+aJsm}14 zSLg#7>KC(s-izw>`b~17MSpqLrLvF%cybU(vE~8m!8|pq5O)^1m4+UD9$Pu?423PWn^~ z7ZITPqsRE9h7jus|# zW_l~9>PYUkZbFS%aWlqXDbB!i@+?A>S*Q}Fs1(cWHmpeE;RbEwzAu*n4UCmfF6t zi91a!#bQAt;nVb$GUE_f!^I2Y$Dk>Kuo5dydEwcA;#+m?UyzHois9I+r#Uwi%e+s< z8EYmzMwLO|+Zd5jd>bVa-D3@OX6QqLk&iBPtZ;&V2W#|Gr-5Rs(nAZ-$*_~8Rj4FT z$iVt<<~V~kN|oUS`Tr2=^>Zm#$^73!pK5$Vm4(g{H4Mc1&nRN_Z?iB4q@V>W2wMqPp?0Y0S zH(VmIq^lNfMKM{+{1K4Top0Fnnuz%&AfGoY_dEX6|$*9R;S2GAXqmD}GwyO^mZ9oMEMLvBz?&5k~= zXw?^Dad0ND01ZT(ZZXUw;LOktY=9?Zh41B!AEKfmL++{S^hzrf6f800&E#bbVXHAv zjAFTqqlJOS65q?#*eUh1OeW-}E!eI6SwL*+^;6MY#Vb@Pa@AGm3rtQ% z8e!#@-vYGzEblV@x|rn?5I3(oWLQdI-Rq(^c_fJWCGg-x_@`R0$m zHWhb&_VSasqT=#mme4r>u^!6^Dx_TBM z%)e$P!6CXSEIwUO^iD@^qM8M8o#Y|vJbTOb9>y=*Dc+%k&2?=_om{HcMxb?~5Kx)s z`F$Q~M|K|XnUi{#+O{R+2U^py(_&BL85k#i-^e zhgFB<_c^2UwHL+r>;8@;Y&t#_dpufUH&&JsI}YxEJzB-i_iJW_3?`4E^wmQRzc74y`}%+>`+KTUMUlGZbHH301$Q20 zvWNUoRAdJgw*S*TpL@X_UkqWlH;_-;AJ0_SNo<0w?AR}i@3J3B#r>(-q8>(X%5ZlM zan22D%>IMT%xDl{N0YnxM(^HPyyp{LAikL{*)XlEhM@|(_cc49TGg@t$69%E1jq`NsO^_x--Z5{RuJFQW>)T`vm{aL21w}6yo8P?+gK{+u;@%oE4 z^3t;mFxAB>wS`$wcaN=VD+ScLjp}Zmni!J<^A1X+Usi+9jqGp#wfstYZKJz2ZzoIk zw%`=`0ivF%v!&zZuxE)i5Sh~Ssx_1--UaZ2z#Z;zyRsRZeTi^gU@U{l6utaoj{xs< zhGWjARKOVH0GcICCNFce=KLC1zX{j}j^&*n!nc@!5pgnqf}eA&N)PingahkgK=?h; z{8FO}l}Yq5aLhOG5dI-}LJch6W7C~}vBYF=J4VF{fBu8llKb1x7qsg+^cg7cZ%dUm zKJg8)0qt&AV7$umetlMT#)I3wf10ydZrDBoY+$B2XQ2DW+%h3TuNs!5opP#wD1@f8 zM6#w$plV>^fhb+SKPZ&rS^-6$v53`^a$&-kt3g?e#AUB&fnzrUoBHa$ao9zrM!e#H zR2Yl|m;GI?)E66fFcFmZ9WWtuCRI^{FI|J;!Pm%cP)fSD%*eP4Dl@arQ&N}0`kmcx z1C6AIk*d!Kqf^lL2z|%iNqrSow4#~$dFi(eDmUpQibBo}qjdzN7g<2LNBdDW(AWRh z!j;D*mF@8hLLzCR;+~4Q1k6yVXik~pl9}R$Xo@;+VSri8Xg%xMh^U09sku|H6i~^v zQhaTi?W3tBWsB1*+pN?kbJUln%{%mY{~hkRob$Wq{O<4fJ?Eb9cdgMv!41H8eVDL` zlnu!07`W6iPe)$hiz8#x<~&EX0C)`#wXBP+wc0>1V=Wd`D+6vPU;Hq&4Y&nN{%C9a zdRqbk&NVo@4)BjBl6oUPQ`iXzo)uHW3|!ji3qk-w7?p08{&R8r(!NIE3|`@uzb-ly zo7ngUI~aZn^Sni|2Qjc;e~3cred5Zpn6V|7$8BtFTKpo@hWfRs!6GC7prdFm(ZSxJ zNG09pHm7lwK%f6yzxnE`TIRzFe3Tw??&oKdU{22P*H+ks*)C{JyKOjvt^(66DU7XM z5z?ki^EE%jl&K8}hL>$6AW9?h!>`(wE$cO4Apsm;yljyBu(T~qdZObwn6(Lm>&gTt z_NN_nBE#una6NUz{x)|=XZ#`(UAxDvem?5$JYx5PrZd<$%CGX^L2Yw9e4f_(XIzf zzu#S+KgyT;SRDNEzBYugT>HsRK(j#V?7XRpO3@pB4^jpqZ`^78>OSxID%`voPa=%G z99Y*6?iENCrJq_iv0}(#1e}{s$<>Euz1mriQ;tMWW69k!cFN!22`Bn5G9W=G$Jsxj z*2+=59?oSt%^;vzQ&A1Rm}bG&-3o&OOJ6$hMO3xfK{!;2y%D6`fh%yG!KTTFDLE8n zQZpI`)%RWioc5WVgpD%e3zh?c@MQs@uH1^gsi2G+NHA?Y&=qi5IG&%iM*RTzJ>R@v|$p4`gpD#V{ zx!T8B+5>+BwhTjS8@gF~9$e|Ua{fmGlpo{&pu55>PFRCd%!aIvs0%ge<*!cW+N(Zb zfq0I`=iZ{iJZ^>=q4O+HK{&eeQWFFwGJtN)4%roFlLwBJ5`Bm~SnIW6 zcrp9(DE*!t0i{5l1ZoT3qK5G=hzKb9hI@QzX(d3i0?4)uU;90*h<@qALhc(RKq(3T z#61CxQ$HjJEhh>I!ecZEA6E+(n;dYILuakM2Uk zSu~Iw8rgJ>IX&=G1hn+3<4`>tXeT+OQcCV*5ZBa07l71(U+{xF7{A z^v)M{6to_3(gk^6cdUAI!r72l*@TJpKsTxApeNL2!RvY3%DJnx1T>SZ!_wrL@s{?? z;9O-rSwBk*gqR9jh?rESZ!R3vBYnoODD4puykG|oiEeM;k?8NWyk)|7U!sR<16;8L z4>&Q5zF@R`V`zVGU-vxhg;VStBb|J#$&aX~Som^&=-nHauGy>0&ZD8fJZa$v#@g`Z z=d4}?mWJ8`5vuJ#*h=WqEFeEhG~DBpn+S}BF;5XXhi=)kzrFf+mqhH>ZIA_H$(xBa zu~#J+G(sQH4KCf@)_m#FmR| zq;ztvN!waZB49Rxqqjqbg?=92G6pHh#J@TZOLIs_l&CnB15VE6khFHvfDlYB;oSJw zr-LFD<1!j%{OxlPR5Hz|8U}9&Z8tdmOh{vRk5QjinRrPE={*yzx=yJW8Vr2AZfi>F zWj4mNPjNXd!a)h~P5Up#4|ErLJWM-fNd0;u&?l>2RZo`ga?=ewx&J+0)xGGDmM*p1Fv0_B1|n)#$^+~N2dkx3 zOuNxH^p+NdAu}%&Ih2_)7V%&{^H9eXY0p-u6Ig?-HfP>t$1qB#ceZ}qAH{TN^7)}X z$0jgMWWL2UrDwtOnvo{7z$!OU``5lzC$Ej%znTv>ai0{wP5yA?i#mB@mR@q0g;ujvRBt5=$_$2yVYHfP9)SOq5wsPV)Bdf%A&NP+n zp57}@GdCuUT^5-Y4d*2H?N`2Rps&6z?v83^qv`-G1E~Ma-!N;w=e11d6tIN^K|8mH K_;vUs3jYV(AFvDn literal 0 HcmV?d00001 diff --git a/projects.json b/projects.json index 477cc4c4..d3aaad1b 100644 --- a/projects.json +++ b/projects.json @@ -139,6 +139,14 @@ "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_dark_mode.webp", "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_light_mode.webp" } + }, + { + "text": "NMKR", + "link": "https://nmkr.io", + "logo": { + "dark": "https://raw.githubusercontent.com/PatrickTobler/koios-artifacts/main/images/projects/nmkr_logo.png", + "light": "https://raw.githubusercontent.com/PatrickTobler/koios-artifacts/main/images/projects/nmkr_logo.png" + } } ] } From 09f3820c0caff994af435d103808529fb1c534f7 Mon Sep 17 00:00:00 2001 From: DCOne Crypto <106427209+DCOneCrypto@users.noreply.github.com> Date: Fri, 21 Jul 2023 11:55:19 +0700 Subject: [PATCH 74/86] Added DCOneCrypto to projects.json and uploaded logo (#213) --------- Co-authored-by: RdLrT <3169068+rdlrt@users.noreply.github.com> --- images/projects/DCOneCrypto.png | Bin 0 -> 35460 bytes projects.json | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 images/projects/DCOneCrypto.png diff --git a/images/projects/DCOneCrypto.png b/images/projects/DCOneCrypto.png new file mode 100644 index 0000000000000000000000000000000000000000..cb6cb23e54d0703a88ff9777f63d1c49868b4f05 GIT binary patch literal 35460 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 b+1!a()vr8yd5dDXJ0yJ_liTGt?_&QiO~DaG literal 0 HcmV?d00001 diff --git a/projects.json b/projects.json index d3aaad1b..1174fb20 100644 --- a/projects.json +++ b/projects.json @@ -147,6 +147,14 @@ "dark": "https://raw.githubusercontent.com/PatrickTobler/koios-artifacts/main/images/projects/nmkr_logo.png", "light": "https://raw.githubusercontent.com/PatrickTobler/koios-artifacts/main/images/projects/nmkr_logo.png" } + }, + { + "text": "DCOne Crypto", + "link": "https://dconecrypto.finance", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/DCOneCrypto.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/DCOneCrypto.png" + } } ] } From 3b76dc8d4741c07f56e9619043ad9075da865e03 Mon Sep 17 00:00:00 2001 From: rdlrt <3169068+rdlrt@users.noreply.github.com> Date: Fri, 21 Jul 2023 06:06:05 +0000 Subject: [PATCH 75/86] Re-arrange projects alphabetically and add Ray Wallet --- images/projects/DCOneCrypto.png | Bin 35460 -> 43286 bytes images/projects/cf_dark.svg | 112 +++++++++++++++++++++++++++ images/projects/cf_light.svg | 112 +++++++++++++++++++++++++++ images/projects/raywallet.png | Bin 0 -> 35653 bytes images/projects/vespr_dark_mode.webp | Bin 528 -> 10088 bytes projects.json | 60 ++++++++------ 6 files changed, 262 insertions(+), 22 deletions(-) create mode 100644 images/projects/cf_dark.svg create mode 100644 images/projects/cf_light.svg create mode 100644 images/projects/raywallet.png diff --git a/images/projects/DCOneCrypto.png b/images/projects/DCOneCrypto.png index cb6cb23e54d0703a88ff9777f63d1c49868b4f05..57bafe0a32e25a26531555259c950050e71e1bae 100644 GIT binary patch literal 43286 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

    MH}H{GM=urBhN$P?2tsZV`}9L3$-by1V;_fPi#& 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 b+1!a()vr8yd5dDXJ0yJ_liTGt?_&QiO~DaG diff --git a/images/projects/cf_dark.svg b/images/projects/cf_dark.svg new file mode 100644 index 00000000..6144319e --- /dev/null +++ b/images/projects/cf_dark.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/projects/cf_light.svg b/images/projects/cf_light.svg new file mode 100644 index 00000000..e924fed5 --- /dev/null +++ b/images/projects/cf_light.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/projects/raywallet.png b/images/projects/raywallet.png new file mode 100644 index 0000000000000000000000000000000000000000..6173e3fa112907455a3777f3bc55a3643af335b8 GIT binary patch literal 35653 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 literal 0 HcmV?d00001 diff --git a/images/projects/vespr_dark_mode.webp b/images/projects/vespr_dark_mode.webp index 4642d8ce01a272251f1eaef76c9f9a33ad4efdf1..4793e917582b252c1c7484a70366f936085b14c7 100644 GIT binary patch literal 10088 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 KZU4*CpOpe|7&{LD literal 528 zcmWIYbaUfiVqge&bqWXzuu%BJ$iQG=#W0sqi-C!O$<9!s$7PY?LID;A1!k5l+Y5GY z=v~0_jq&|9Kj-^7bK3XawB)bdR3!I1<%#XLlP9X*PM+}nwz}e|^q>ruYYYtk{(o;| Gzy| Date: Fri, 21 Jul 2023 08:29:40 +0100 Subject: [PATCH 76/86] Updated VESPR images (#215) * updated vespr images --- images/projects/vespr_dark.webp | Bin 0 -> 6368 bytes images/projects/vespr_dark_mode.webp | Bin 10088 -> 0 bytes images/projects/vespr_light.webp | Bin 0 -> 9902 bytes images/projects/vespr_light_mode.webp | Bin 8198 -> 0 bytes projects.json | 4 ++-- 5 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 images/projects/vespr_dark.webp delete mode 100644 images/projects/vespr_dark_mode.webp create mode 100644 images/projects/vespr_light.webp delete mode 100644 images/projects/vespr_light_mode.webp diff --git a/images/projects/vespr_dark.webp b/images/projects/vespr_dark.webp new file mode 100644 index 0000000000000000000000000000000000000000..10112bb17e26ec2cc1a047e478e25269f468624d GIT binary patch literal 6368 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 iD|+SrFZX}B|I7Vf?*DTCm;1lm|KA%JO?cO;M z(f zbEHU|hruxm!XZf|$w}q{2r0Y5iWEi1GA~yIKyg{NUJScht#%<-S#jL~&2zK>61QC! z<)I`%N%;aaw7fTxv}lqSMYp)>nyUjWKtnfR-4023T)Lspu5Ob_k_Vt0`oV%PbkVIV zQeSk4g-GH@H)wyG@L;SCks{_DZQJ3qqR zA%M_Gk|QZ=033gK!QuSfoBmHgz*a;E1QLJ%0*F8Xga8l-Ab=3C9RdP~hyW1LLO2Tu z0AU~^0sz9Ef(S%71`yVOfB+~c2pEO|7%&9TVH+Ue9B6+70e}J+pcpVL!G1AZGw(_^uJ~jeij?-(@xc)vN2PhyalF`6VK1)w|az)EE)TA|kAOI*sIr zEDDD_p7Q}RsU840Yrr5{0qks z)mlGQ)$Rfi79v8}5kU445y^=^fQW#FunhoUKkta-tcT4W_udh!!ZI3m!W9L1Y5_|` zXM}J7zVYNAM4$^iP5@ng6%jr3ARfQNIeZa7^aMWNwJpI(-D5N4EWrhqfE~d=#=a;Q%GeGI z@_v^rHdTJ@JCYAoOYZMC$%k>p_2aZ30Smk z1&MB9gp70V^PvyK{Qt}Gg6jhVQ&~P3Bp{h&|12m5H-nTLf^B#*GbRAGVaN;l4roIT zV~9u?1`tFX4+g+CJPRDyif08x!DUB5G0fUd0Jh^|sbF9MTE>C;blxN1YwTpP+; z=oNOCs3{o3FrzZeU>H>)x7&KK3XGyW5Eusa;S@Qdn8p}W2t-4x1V?i(#N30_!GLfvi9vn97KloH7t$&B%pnrv_JMZr=BM z$(oqwquSIofq?8XFB)0#&9&JV<5#ad%P=W6;GqGpBzoM^R-3}m zmKcw3HwW9e8(Bilgq0< zJZRF9y=K)Of)(}hcUM^)~i|~7*QdZMhL30qW z9ozyU%Xw2qxCn@-cEdoV`shwOJs3WJw=h`}#uz;`aHV`8vqYFF8|6NM(lXty*B?AQ z`vDF)WtI9#V%UmVNFH`5?$zKRi#IJqn7X=z$B|{0u&L{8C9lGC zYC3^|YRj90p#16!2*dbJsOdbcB>^|EzHmUo*DXD}4^gt6hp0_Wfg+EsP@A-LrcxSj zu>)Rh6+RC*^R(FccCjTgb_=~=JiY}Y8N`YhfO`71if3*ZnoOQvJ?{3|`lcnh4J1>0 zk2?&3V(BFqlj8idor!aKn`>69oN5+BAyH4z;ULU;33iR|xLd>QVMZeybx|RwwgX~t zVSH#tLm&pdl>#6=c-W>8UC>9=`Y48Y0(#>K1E`$aO&PPWOfZ>cP3pxEuQXK9m{(Cx zCL>ZJl4x71Cah-G^GF#C7<%<&dGwWKimcb`KI{dTDsrS@5K(1|>)H1UlNHviYs7qK!)-!t19LXbPFi7%Zh(%TsAqH$iA(TmV`|PRaB07xWyz4F+=53y=wd@4nP##vB zTF!`QZSmVFS_hyl${|dNpRy!IZkPgc2PQ6qL?uwP{q%2NAQ2;$ASx`hqHI7uP}u7n zc-E34#Z+bMl@H!iA4q5x@^mid1~WpVf+pWFn;8r{L{4Nur|ezO&vRm`15$Z z<{6{trHadYCpZv{<{brA`s_VQQ9v+Mrj1IT6@q6tlY&w0fM5_XtR-#LyT)DVX=VSx>9h$b?{?lHmDBS{wn4w$8?H3|}IILYE5{bZsno?_e<^ zwyS+Fp9MLv=ixXe8!S0?tR=D)(vIR;N0PcFbK=%Jm>m*Z+=aa`JiO*Cd;N?qwdl7f z45JW=r-@3*n{05rRvAW&?k0e0gkKQGJuEz1I$9c zE)m4#r#AI*o644Y*DN<3y2&Rvfb#=l9K&GkR@>jxBmg(f(VJ0KQb}5stKsmnz*C)X z7&JfY;SV$#5|(3ukkePYlV)Jr#YwsYRp>-+@?}Mg;we5p_C?7iM)sI>Sq=6^5+uq% z%_*8|$D;p)Z znV5R(N2VR12oxah2VQ1>J-%@JaTKp5`u0TjwvMsF+P+XM5;lZ|Vi9MMD$gX80fVH@ zn`da)I!zy?lKv&F%S{6 zLmAxs9+Vp!1W-gON*F|Xb9zvE$Cwv~zNc055L)(5UIwxLav_<-bd8-lA)~pjDvVZL zfoF_fjAD@5<>e89d&CYAh z9+;(e6Nq&As!{@Fx{6{eUZtZ24weROeWX?d0 z8AwM!LChlO`ML>($mFQozNC7}T%aH!1Fr+q>qr=wq=gJEqU0(kmeV89Hdgj`-l*Y- z!2>Z2Gnf!W(2Y<7n=6p4U#VgCk0}5d+%Ff;ajKh4Mf0oBp>BR;d&p1pV~!*k?OB8a z3-mof*SPYh|}tu|sDH(}s;Of!Z7tm=bd$Ok1)l@Fyy8Lw$LDcptWD?p*5h+;d8hI3@f z42Ed^US+!iZo%KFjj`9DpoT+w;Tfji59x>Ve1Y(uqaR55QUwRPhN=Px!DP_8{6-I9=mTtLhLj!23$^ zd=3MXy$9wxfm^7xqg!Br8}RhQpFg&Gk`q_wFihL^Z9)&}rIM;Tn3_P07*6>)1w+X6 z??2?a)2gvaapV4-kkV;Ak2@cS3L~)WMcd0H_0v z9dR)0k>tKdUU?f7bg;_PBuk|m&~Uhr$Z|Bv`Gn++pImmH$JuDMhA`)?=6~d;;`M(% zCFiJ|P{;}>DwtNf0i(iisLbASX)>{ezFw0)&pfG{E|&NY*DFM7XZP1vKluHhPWY1d zQKSnZ^tH&^Cvn=ycmFK_pcaR-9v2Z~RqUp&-zX}c^?LbYXl)0dDHrp@V0pU_rJk9XhJN&RJC6= zeoHc*hu`G+b&4+pxaw{*UN?~^4&)C$AQ z^5Q!$G4+AL@Uv~1pf81~`u`{JWtc4#aF}I8TK!mME3LPSU#@Q-^u=4cUq1Sr6_$lH z=S}Ujek)#L^fG<-@${|BwmbULIIjNrbT_b5#fvm8+Z2wf?rMHylH)s9A zD7qMB44or{OT@At^hy|jXuxu~v_)?!FBZmxv^BQ0DYVddRsKaE23eBM7Jn7_-#}l8 z%LbK2<&SRpJusASF%tw+-Jz5SqXg!OFVxOFk5r@r=?EK(J=bNlP>0K7&oT{fSQ)y1 zZIkB{;DS65%fBkJ*Lg||ILvFGn=JTwzh+i%Iv*MtA ztm7lR61<7-kR3~4Fek0dKzyRWHl?!IRE_;A!GC*N+r*pJn+<#n3P5`*TTQb?8Tlvx zy5Yr`i@u<+Z2;7)3(rFpzWI0x%(N{%N-1({;|*C}EaO&nlycr-RMhspwxnWG<|oR- zKNc6`lz5Z8UWIRx>%_F(3<{#ZG_JKH zD6*ZF!PeL;n3;RN`_7NQ1KzN00O-a7mk}HW6hE~|VRq1Cp2Y)0a6K_to4x0Ik|$`AGaZ`LKu7XSD4Yr;o|P zW%&Q~zMsA{KdAq`r}b~D*%fAF|F}w>$KlSGcTZJsDPKPJoPB5Ay8bOnnT5O^g%v~h z#L>RSE-xQ{AAe#sS{$hzZCm1jPtWr3XR%Vxlt6RBD~YEw`&@OqKiwisN;235YJ zemG6{erAgiy?~D3p=~@BHhvw(E))3VWcAr+r&1&cc#VvXV&6v&u|d2>jOKCgh$7EU zk`&<=;oC|)D3ZaVst^DLEaBg$+W9EaXq7URm3{=XO3EdQ!+sSDfJTJ?_s*LH;v%s@ zvbod(t)t^hcRCe!2yk_wKCGg&V)_=s1f~v>VCQ~}qUzPh4TefHm?P#>wGrAwpSfA! zW}S$v@Q^3o)A;C@N6$Lm2@D*PbkcdR$4}eQDiXGYyKXZ$@7)^`QgtpeM6-?}-g<&c zzg^>8{~|GUKHa}B=ZnY=PnWZo{P*)iP)(!RXC22-abrZ!;q?+!2|Bqez^ZqDWeyCx z-{sLV6%e?~SHJZEl_@7T+l9sTZugR&6bVYaR)A$@w0C8+05X2F3 z1?f%)b(PeKGoG9q8u8-}!!ZhpDRSkI0_UorfmmNA6^W!ERxdJseEqw>Z)AP#bqY(g zNKT>DqYM$TL*a_kWvl4D;3wF$$#p3rTRgz8magkYl>oA^8B6!7*4Wlz=F``=A(FHh z@G5!kY)2WPkGCL!BG5qHkh!!^O`|sE_T$Yb8dNV73V<@b_Ir-qvk=+j2H-h9&?#W~(lB>NA{t9l zSwzV5pag^1Hh`sr<`n;HJ@ppQi{~MZcXwF)zzkx8`!|M`i0TC8eB+pq=GeiE)^9#o z4i8@(0FV3r6Br-=MqDi02eT4Zm`4Zvd_$` zFS+gpDH<3)*8q_SVYSl0SSv*fFOPE~?qhecOIPlpxKk6F^bNaFo{!0`@&zaC!FWLrB~(OTaJmc8nB2#YC=v zQgHJ?C2SK38cl2i`*z!S;Do%c>kvum8DN7%ATiQtIdv%=`_UZjaRdO{Ame5gPlbAO zrY6Cf7U;6Do6U6tCL-8bqHHdkWn5esc=>>!nFcaR2E@xFB=Ha&F|I4A9P~u7AtVgL)WOo?DK+#w1gt6l<7EgY3 z_Tv5jevz0d#WNLjx0wNJ0keqI;ri&aOn?G(guMfJcFr#r_nSk*lnn6u&p~cTrptcn zesTR*f!yHETmU32whh{YM8ahA+rK9>dk@fk_k`YcT{(5$JXa(^&fKIHdOC+H!BtR-+wFvNHEBq`)qEDyVVmvXa1r+AIUL62eVk_X2F3L}`c)lXtN&Fc+j3%Z_Z4 zsBLNruEYI-l`cv!#=ovZ+nZHt5Py$QZyFW21`5!CEv*dT_8DO|V!Hx>^Rz6BD@!mP z@92i>V;mAuw`YZy#wFzs600-RkpNt5D8V{`tBg{m-IeZ8bifXkwX1cg{tl?1|0dy1 z?%BZiq5xjwv`~#!r)Ua|O|raodt{m}678*R=hxKAe#7*P$44f)D^5pN689&bc^Vej&O1SnmJ9C7sj&_-|c662g-?2f+Iog(# zDHK*tJaD%m>7m4Ecgp|&EG>6Xv_&Aa&cnmc?{15PX-gPVsZ~eWva852voK0l$Q!;B z{n`C2@sbhFQWs!NZHl64e~-R+YD)gF3p$lW18|`!LNxP~1-eU;N{=$YL97VJZeNpD z1R$+(!y!e2VmVuYKTiWlAcvDv(^-WAtVp_kyPCUBtVY7fo3T|^SSuSDW+rFMysMyy zlG??8TrZ0febb|y2V5uu5GR`MR66}=3DlACrfGJL)8HNN!oChPcd&CHEe&ktZQbgl|>+kLCcHuP60Hw7hWKj{$ zbM|&j4kpO$Cx%PwC83bBYd`WhU7R8jHc?JKg-abt)8 z-`WVIWa6*)W>%0V1%ta>-#4z_0_)(?fG{V)%Ub1!CnoopaRyYgP;JA4dk+|Cp5L>K}j(`_So)T;(iz#$3 zglvg-)XU_2XJk7lw?DXc)bES0nZMuTUaHWZ6oUO`&{tDIG)UQeBO5G9RknEt0itG9CdIQRYSkgn>iIO>%dkCcLH*~%)=Yf$`tq} zy>!k;_*eHL?E;`vy&rXrNGyrKBydgfYm=P?eq0{>V{q^iyZm|O27H7avUZh7hP-vaTCZgtCf56=eiO5`>oUdv46i*131$zZZc97x^R;rCSI``%(V^udw39# zbfPls`CRH;HW=mGa47MK<#X74Ytz?Wau`_jSbB&Kz2p8w#f%2T+BMMeCd5oS>Xe!5 zWY;lS68xb%>|X zu0udT2LM&Cfg*we(I|Sd#2)7fb!xu-Sm2f)Vn8fHM*z%YO!j4R8ksUZeaJ?;bmA8I}(RcU1)YMoM;g~O^s_-Y%wJdYfVS zi=s0Od9QZOQ?0kExI`83%)E>@?I%l zdy59{x37ng^ZuH-5kR1T=H%QI6J)%!nB&J!9MjYZ+Q~bbfE)@P5~_EydNeFf!v-Yg z(f2PcU=TyOkgZ=msE^cDa7q&<)G&2cvG3QLxtFfoJ~#&xM9aD^G+nqbTnbQf}^*|(o&|ix6-p#yNz{q3jc9^uHPU<%-;C9ICq-JhxIpgH=9RKM0W>{24eN( zl4I0#zgRV^3j--HJ-gFNYV~ENpCa zVTZt8hshcL7sxV$k4mSgYPf4R{^O%xsGOlokHNHz_b) zKN=B#pc(*3G%*-hse$v%X!TG>r<#GpSQDJQx4*%z9}E(EVzK|`$wg+Gz^s8Tt`WLC zIoudifA$^8y&PQsQvT27)W3G7TfA2~u;}-c)sqX#%`~-*_T2kH0BnzY64)WI9;yu+ zt0=}RMnpcbIwS^%;~)RP4ue^@g;7C%>bQ?xk(gFI1}r1!Q0( zCl5Z1f|-^!xZVC7^U>j~1;}Jm+=;ZS?t!zQL_(P>y0?$&7It82k(%MJs9LwPTWqK! za0{%iVR?;Qev!u4NRrPNObWq3EsTQ|0o6mWajFI8?1n*~JDfBJ`!97MhW!{fF>XKp zG=dWD;9$fu#xdAMzdw~Cl$*|@6~cBDc8da>PQm}F-;Dc&T{Uno0HtH6LfNgtB#fC0 z59dDeNtJm?>UBA$W5-O%_HMEKtILnfUQyj#jmDH(CppXT_QsEoo-<+C z#|L+X^|b9TMcN*2+kKf??e@N878F00{+_CRW^P%PdAL=a1Pva~oIkO?|5$_6I3hVu z#VV~#R>(d*gssh<$2?-2m*yV8vH!}K zm_3}S_8p-tfI0fDwnbvsO)&m3Z$0os^Sk;z(D&h1smAW#7P9wE0=onAXT`QU#oH9? zk@7btYdjrjZv!h_0xv;>uQr`9`b4&Om5I}A0X<*UzEJ;+v`8K7?d#@3<{ZmVrS+6T zbl1__cJ-Z~=7)~;U33Nqd$_-3n{_zck?KYKh}AwB*aD>uIMW5>Tgp8)=@Wr0MCXYD zOb9{dI=`{C*B-+5MRtSaU=f%qYDB5Uw^zcPTqdPUVQ)IN{JzrJFZl)0?13o2bew|g zbMvpG>e8Q_VUP|7Fm;r7YO$=1=nm!wjH2Zob{KDqRA0MYVR{z=z3rG& zAwXlwb-y(Y+v0FvzC=RzyV~c4sr|fP(O+@jc~F2Ug7b4Amvg5TK>%zh~ab56a(3a)Kt(7(pble&ZZd~8RT4+eioqE!FP3TVw2fS9WjHR{(!?{nqE_Um=aY_044 KZU4*CpOpe|7&{LD diff --git a/images/projects/vespr_light.webp b/images/projects/vespr_light.webp new file mode 100644 index 0000000000000000000000000000000000000000..4d768a555cf1374c425b6fc387de5a7036a8af7a GIT binary patch literal 9902 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 diff --git a/projects.json b/projects.json index ed57c055..84e59f19 100644 --- a/projects.json +++ b/projects.json @@ -168,8 +168,8 @@ "text": "VESPR", "link": "https://vespr.xyz", "logo": { - "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_dark_mode.webp", - "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_light_mode.webp" + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_dark.webp", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/vespr_light.webp" } } ] From 6d8a8be300fb5ea4bd70bc7402ae2d7d4517ab96 Mon Sep 17 00:00:00 2001 From: "Ola [AHLNET]" Date: Fri, 21 Jul 2023 14:19:42 +0200 Subject: [PATCH 77/86] fix for tip check in cron jobs (#217) closes #216 --- files/grest/cron/jobs/active-stake-cache-update.sh | 4 ++-- files/grest/cron/jobs/asset-info-cache-update.sh | 4 ++-- files/grest/cron/jobs/epoch-info-cache-update.sh | 4 ++-- files/grest/cron/jobs/pool-history-cache-update.sh | 4 ++-- .../grest/cron/jobs/stake-distribution-new-accounts-update.sh | 4 ++-- files/grest/cron/jobs/stake-distribution-update.sh | 4 ++-- files/grest/cron/jobs/stake-snapshot-cache.sh | 4 ++-- projects.json | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/files/grest/cron/jobs/active-stake-cache-update.sh b/files/grest/cron/jobs/active-stake-cache-update.sh index 1d7a8c8b..d85dd4e4 100644 --- a/files/grest/cron/jobs/active-stake-cache-update.sh +++ b/files/grest/cron/jobs/active-stake-cache-update.sh @@ -1,9 +1,9 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select time from block order by id desc limit 1;") +tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) -if [[ $(( $(date +%s) - $(TZ=UTC date --date="${tip}" +%s) )) -gt 300 ]]; then +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi diff --git a/files/grest/cron/jobs/asset-info-cache-update.sh b/files/grest/cron/jobs/asset-info-cache-update.sh index 8a458054..3b6afc47 100644 --- a/files/grest/cron/jobs/asset-info-cache-update.sh +++ b/files/grest/cron/jobs/asset-info-cache-update.sh @@ -1,9 +1,9 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select time from block order by id desc limit 1;") +tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) -if [[ $(( $(date +%s) - $(TZ=UTC date --date="${tip}" +%s) )) -gt 300 ]]; then +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi diff --git a/files/grest/cron/jobs/epoch-info-cache-update.sh b/files/grest/cron/jobs/epoch-info-cache-update.sh index 3a07552d..19012f81 100644 --- a/files/grest/cron/jobs/epoch-info-cache-update.sh +++ b/files/grest/cron/jobs/epoch-info-cache-update.sh @@ -1,9 +1,9 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select time from block order by id desc limit 1;") +tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) -if [[ $(( $(date +%s) - $(TZ=UTC date --date="${tip}" +%s) )) -gt 300 ]]; then +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi diff --git a/files/grest/cron/jobs/pool-history-cache-update.sh b/files/grest/cron/jobs/pool-history-cache-update.sh index 2435ddd3..7bc75ee4 100644 --- a/files/grest/cron/jobs/pool-history-cache-update.sh +++ b/files/grest/cron/jobs/pool-history-cache-update.sh @@ -1,9 +1,9 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select time from block order by id desc limit 1;") +tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) -if [[ $(( $(date +%s) - $(TZ=UTC date --date="${tip}" +%s) )) -gt 300 ]]; then +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi diff --git a/files/grest/cron/jobs/stake-distribution-new-accounts-update.sh b/files/grest/cron/jobs/stake-distribution-new-accounts-update.sh index e4c5253d..70f3d0d9 100644 --- a/files/grest/cron/jobs/stake-distribution-new-accounts-update.sh +++ b/files/grest/cron/jobs/stake-distribution-new-accounts-update.sh @@ -1,9 +1,9 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select time from block order by id desc limit 1;") +tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) -if [[ $(( $(date +%s) - $(TZ=UTC date --date="${tip}" +%s) )) -gt 300 ]]; then +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi diff --git a/files/grest/cron/jobs/stake-distribution-update.sh b/files/grest/cron/jobs/stake-distribution-update.sh index f96c84d8..3447dee0 100644 --- a/files/grest/cron/jobs/stake-distribution-update.sh +++ b/files/grest/cron/jobs/stake-distribution-update.sh @@ -1,9 +1,9 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select time from block order by id desc limit 1;") +tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) -if [[ $(( $(date +%s) - $(TZ=UTC date --date="${tip}" +%s) )) -gt 300 ]]; then +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi diff --git a/files/grest/cron/jobs/stake-snapshot-cache.sh b/files/grest/cron/jobs/stake-snapshot-cache.sh index 307f3d7d..44549802 100644 --- a/files/grest/cron/jobs/stake-snapshot-cache.sh +++ b/files/grest/cron/jobs/stake-snapshot-cache.sh @@ -1,9 +1,9 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select time from block order by id desc limit 1;") +tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) -if [[ $(( $(date +%s) - $(TZ=UTC date --date="${tip}" +%s) )) -gt 300 ]]; then +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi diff --git a/projects.json b/projects.json index 84e59f19..9fddf57f 100644 --- a/projects.json +++ b/projects.json @@ -70,7 +70,7 @@ }, { "text": "Cardano Foundation", - "link": "https://cardanofoundation.org/https://cardanofoundation.org/", + "link": "https://cardanofoundation.org/", "logo": { "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/cf_dark.svg", "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/cf_light.svg" From f1e2d9e34cbe9d8241b4e816b7f36f2b3947c540 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Wed, 2 Aug 2023 18:32:33 +1000 Subject: [PATCH 78/86] Add SQL Format updates as per SQLFluff guidelines (#226) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Fix Asset Info Cache (include mint/burn tx rather than sum for meta consideration) 2. Update SQLs as per SQLFluff linting guidelines: - [x] 00_blockchain - [x] 01_cached_tables - [x] 02_indexes - [x] account - [x] address - [x] assets - [x] blocks - [x] epoch - [x] pool - [x] script - [x] transactions - [x] views 3. Fix `_last_active_stake_validated_epoch` in active_stake_cache (#222) 4. Typo for `pool_history_cache.sql` as well as add a check to ensure epoch_info_cache has run at least once prior to pool_history_cache (#223) 5. Move control_table entry in cache tables to the end (instead of start). ## Where should the reviewer start? Not really sure, too big to drill down 🙁 , we shouldnt have added any functional changes as part of linting. I expect us to cover review as part of tests itself ## Motivation and context Across months, different folks added SQL updates using different patterns, making it difficult to align/scan/automate linting ## How has this been tested? Was tested on guildnet as per below: - [x] Run `./setup-grest.sh -r -b lint` - [x] Wait for cache to be re-populated - [x] Run schemathesis tests --- .gitignore | 1 + files/grest/.sqlfluff | 18 + files/grest/rpc/00_blockchain/genesis.sql | 60 +- .../grest/rpc/00_blockchain/param_updates.sql | 38 +- files/grest/rpc/00_blockchain/tip.sql | 41 +- files/grest/rpc/00_blockchain/totals.sql | 58 +- .../01_cached_tables/active_stake_cache.sql | 262 +++--- .../rpc/01_cached_tables/asset_info_cache.sql | 100 +-- .../01_cached_tables/asset_registry_cache.sql | 98 +-- .../rpc/01_cached_tables/epoch_info_cache.sql | 180 ++--- .../01_cached_tables/pool_history_cache.sql | 481 +++++------ .../rpc/01_cached_tables/pool_info_cache.sql | 529 ++++++------- .../stake_distribution_cache.sql | 310 ++++---- .../stake_distribution_new_accounts.sql | 110 +-- .../01_cached_tables/stake_snapshot_cache.sql | 243 +++--- files/grest/rpc/02_indexes/13_1_00.sql | 2 +- files/grest/rpc/account/account_addresses.sql | 71 +- files/grest/rpc/account/account_assets.sql | 84 +- files/grest/rpc/account/account_history.sql | 39 +- files/grest/rpc/account/account_info.sql | 229 +++--- .../grest/rpc/account/account_info_cached.sql | 115 +-- files/grest/rpc/account/account_rewards.sql | 38 +- files/grest/rpc/account/account_updates.sql | 61 +- files/grest/rpc/account/account_utxos.sql | 40 +- files/grest/rpc/address/address_assets.sql | 43 +- files/grest/rpc/address/address_info.sql | 112 +-- files/grest/rpc/address/address_txs.sql | 33 +- files/grest/rpc/address/credential_txs.sql | 40 +- files/grest/rpc/address/credential_utxos.sql | 23 +- files/grest/rpc/assets/asset_addresses.sql | 39 +- files/grest/rpc/assets/asset_history.sql | 47 +- files/grest/rpc/assets/asset_info.sql | 52 +- files/grest/rpc/assets/asset_info_bulk.sql | 57 +- files/grest/rpc/assets/asset_nft_address.sql | 25 +- files/grest/rpc/assets/asset_summary.sql | 123 ++- files/grest/rpc/assets/asset_txs.sql | 46 +- .../rpc/assets/policy_asset_addresses.sql | 28 +- files/grest/rpc/assets/policy_asset_info.sql | 74 +- files/grest/rpc/assets/policy_asset_list.sql | 28 +- files/grest/rpc/blocks/block_info.sql | 129 ++- files/grest/rpc/blocks/block_txs.sql | 49 +- .../grest/rpc/epoch/epoch_block_protocols.sql | 20 +- files/grest/rpc/epoch/epoch_info.sql | 60 +- files/grest/rpc/epoch/epoch_params.sql | 80 +- files/grest/rpc/pool/pool_blocks.sql | 64 +- files/grest/rpc/pool/pool_delegators.sql | 54 +- .../rpc/pool/pool_delegators_history.sql | 51 +- files/grest/rpc/pool/pool_history.sql | 60 +- files/grest/rpc/pool/pool_info.sql | 82 +- files/grest/rpc/pool/pool_list.sql | 19 +- files/grest/rpc/pool/pool_metadata.sql | 60 +- files/grest/rpc/pool/pool_relays.sql | 38 +- files/grest/rpc/pool/pool_stake_snapshot.sql | 28 +- files/grest/rpc/pool/pool_updates.sql | 104 +-- files/grest/rpc/script/datum_info.sql | 24 +- files/grest/rpc/script/native_script_list.sql | 22 +- files/grest/rpc/script/plutus_script_list.sql | 20 +- files/grest/rpc/script/script_redeemers.sql | 65 +- files/grest/rpc/transactions/tx_info.sql | 745 +++++++++--------- files/grest/rpc/transactions/tx_metadata.sql | 32 +- .../grest/rpc/transactions/tx_metalabels.sql | 23 +- files/grest/rpc/transactions/tx_status.sql | 39 +- files/grest/rpc/transactions/tx_utxos.sql | 104 ++- files/grest/rpc/views/account_list.sql | 8 +- files/grest/rpc/views/asset_list.sql | 14 +- .../grest/rpc/views/asset_token_registry.sql | 23 +- files/grest/rpc/views/blocks.sql | 40 +- specs/results/koiosapi-guild.yaml | 21 +- specs/results/koiosapi-mainnet.yaml | 21 +- specs/results/koiosapi-preprod.yaml | 21 +- specs/results/koiosapi-preview.yaml | 21 +- specs/templates/1-api-info.yaml | 21 +- 72 files changed, 2983 insertions(+), 3157 deletions(-) create mode 100644 files/grest/.sqlfluff diff --git a/.gitignore b/.gitignore index 66983267..e54ffbe0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ tests/**/__pycache__ tests/**/.* .vscode +.swp diff --git a/files/grest/.sqlfluff b/files/grest/.sqlfluff new file mode 100644 index 00000000..22fde660 --- /dev/null +++ b/files/grest/.sqlfluff @@ -0,0 +1,18 @@ +[sqlfluff] +dialect = postgres +exclude_rules = structure.column_order, references.keywords +max_line_length=260 +recurse = 0 +capitalisation_policy = upper +extended_capitalisation_policy = upper +idented_joins = True +indented_using_on = False +tab_space_size = 2 +large_file_skip_byte_limit=35000 + +[sqlfluff:indentation] +tab_space_size = 2 +allow_implicit_indents = True + +[sqlfluff:rules:convention.count_rows] +prefer_count_1 = True diff --git a/files/grest/rpc/00_blockchain/genesis.sql b/files/grest/rpc/00_blockchain/genesis.sql index 9746e282..164f6894 100644 --- a/files/grest/rpc/00_blockchain/genesis.sql +++ b/files/grest/rpc/00_blockchain/genesis.sql @@ -1,36 +1,36 @@ -CREATE FUNCTION grest.genesis () - RETURNS TABLE ( - NETWORKMAGIC varchar, - NETWORKID varchar, - ACTIVESLOTCOEFF varchar, - UPDATEQUORUM varchar, - MAXLOVELACESUPPLY varchar, - EPOCHLENGTH varchar, - SYSTEMSTART integer, - SLOTSPERKESPERIOD varchar, - SLOTLENGTH varchar, - MAXKESREVOLUTIONS varchar, - SECURITYPARAM varchar, - ALONZOGENESIS varchar - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.genesis() +RETURNS TABLE ( + networkmagic varchar, + networkid varchar, + activeslotcoeff varchar, + updatequorum varchar, + maxlovelacesupply varchar, + epochlength varchar, + systemstart integer, + slotsperkesperiod varchar, + slotlength varchar, + maxkesrevolutions varchar, + securityparam varchar, + alonzogenesis varchar +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT - g.NETWORKMAGIC, - g.NETWORKID, - g.ACTIVESLOTCOEFF, - g.UPDATEQUORUM, - g.MAXLOVELACESUPPLY, - g.EPOCHLENGTH, - EXTRACT(epoch from g.SYSTEMSTART::timestamp)::integer, - g.SLOTSPERKESPERIOD, - g.SLOTLENGTH, - g.MAXKESREVOLUTIONS, - g.SECURITYPARAM, - g.ALONZOGENESIS + g.networkmagic, + g.networkid, + g.activeslotcoeff, + g.updatequorum, + g.maxlovelacesupply, + g.epochlength, + EXTRACT(EPOCH FROM g.systemstart::timestamp)::integer, + g.slotsperkesperiod, + g.slotlength, + g.maxkesrevolutions, + g.securityparam, + g.alonzogenesis FROM - grest.genesis g; + grest.genesis AS g; END; $$; diff --git a/files/grest/rpc/00_blockchain/param_updates.sql b/files/grest/rpc/00_blockchain/param_updates.sql index cf6f5595..67ae8782 100644 --- a/files/grest/rpc/00_blockchain/param_updates.sql +++ b/files/grest/rpc/00_blockchain/param_updates.sql @@ -1,20 +1,19 @@ -CREATE OR REPLACE FUNCTION grest.param_updates () - RETURNS TABLE ( - tx_hash text, - block_height word31type, - block_time integer, - epoch_no word31type, - data jsonb - ) - LANGUAGE PLPGSQL - AS $$ - +CREATE OR REPLACE FUNCTION grest.param_updates() +RETURNS TABLE ( + tx_hash text, + block_height word31type, + block_time integer, + epoch_no word31type, + data jsonb +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT DISTINCT ON (pp.registered_tx_id) - ENCODE(t.hash,'hex') as tx_hash, + ENCODE(t.hash,'hex') AS tx_hash, b.block_no AS block_height, - EXTRACT(epoch from b.time)::integer as block_time, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, b.epoch_no, JSONB_STRIP_NULLS(JSONB_BUILD_OBJECT( 'min_fee_a', pp.min_fee_a, @@ -47,13 +46,12 @@ BEGIN 'max_collateral_inputs', pp.max_collateral_inputs, 'coins_per_utxo_size', pp.coins_per_utxo_size )) AS data - FROM - public.param_proposal pp - INNER JOIN tx t ON t.id = pp.registered_tx_id - INNER JOIN block b ON t.block_id = b.id - LEFT JOIN cost_model CM ON CM.id = pp.cost_model_id - ; + FROM + public.param_proposal pp + INNER JOIN tx t ON t.id = pp.registered_tx_id + INNER JOIN block b ON t.block_id = b.id + LEFT JOIN cost_model CM ON CM.id = pp.cost_model_id; END; $$; -COMMENT ON FUNCTION grest.param_updates IS 'Parameter updates applied to the network'; +COMMENT ON FUNCTION grest.param_updates IS 'Parameter updates applied to the network'; -- noqa: LT01 diff --git a/files/grest/rpc/00_blockchain/tip.sql b/files/grest/rpc/00_blockchain/tip.sql index eb4539c5..0fccd977 100644 --- a/files/grest/rpc/00_blockchain/tip.sql +++ b/files/grest/rpc/00_blockchain/tip.sql @@ -1,30 +1,29 @@ -CREATE FUNCTION grest.tip () - RETURNS TABLE ( - hash text, - epoch_no word31type, - abs_slot word63type, - epoch_slot word31type, - block_no word31type, - block_time integer - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.tip() +RETURNS TABLE ( + hash text, + epoch_no word31type, + abs_slot word63type, + epoch_slot word31type, + block_no word31type, + block_time integer +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT - ENCODE(B.HASH::bytea, 'hex') AS BLOCK_HASH, - b.EPOCH_NO AS EPOCH_NO, - b.SLOT_NO AS ABS_SLOT, - b.EPOCH_SLOT_NO AS EPOCH_SLOT, - b.BLOCK_NO, - EXTRACT(EPOCH from b.TIME)::integer + ENCODE(b.hash::bytea, 'hex') AS block_hash, + b.epoch_no AS epoch_no, + b.slot_no AS abs_slot, + b.epoch_slot_no AS epoch_slot, + b.block_no, + EXTRACT(EPOCH FROM b.time)::integer FROM - BLOCK B + block b ORDER BY - B.ID DESC + b.id DESC LIMIT 1; END; $$; -COMMENT ON FUNCTION grest.tip IS 'Get the tip info about the latest block seen by chain'; - +COMMENT ON FUNCTION grest.tip IS 'Get the tip info about the latest block seen by chain'; -- noqa: LT01 diff --git a/files/grest/rpc/00_blockchain/totals.sql b/files/grest/rpc/00_blockchain/totals.sql index bb562f08..63ccc030 100644 --- a/files/grest/rpc/00_blockchain/totals.sql +++ b/files/grest/rpc/00_blockchain/totals.sql @@ -1,36 +1,44 @@ -CREATE FUNCTION grest.totals (_epoch_no numeric DEFAULT NULL) - RETURNS TABLE ( - epoch_no word31type, - circulation text, - treasury text, - reward text, - supply text, - reserves text - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.totals(_epoch_no numeric DEFAULT NULL) +RETURNS TABLE ( + epoch_no word31type, + circulation text, + treasury text, + reward text, + supply text, + reserves text +) +LANGUAGE plpgsql +AS $$ BEGIN IF _epoch_no IS NULL THEN RETURN QUERY ( SELECT - ap.epoch_no, ap.utxo::text, ap.treasury::text, ap.rewards::text, (ap.treasury + ap.rewards + ap.utxo + ap.deposits + ap.fees)::text as supply, ap.reserves::text - FROM - public.ada_pots as ap - ORDER BY - ap.epoch_no DESC) ; + ap.epoch_no, + ap.utxo::text, + ap.treasury::text, + ap.rewards::text, + (ap.treasury + ap.rewards + ap.utxo + ap.deposits + ap.fees)::text AS supply, + ap.reserves::text + FROM + public.ada_pots AS ap + ORDER BY + ap.epoch_no DESC); ELSE RETURN QUERY ( SELECT - ap.epoch_no, ap.utxo::text, ap.treasury::text, ap.rewards::text, (ap.treasury + ap.rewards + ap.utxo + ap.deposits + ap.fees)::text as supply, ap.reserves::text - FROM - public.ada_pots as ap - WHERE - ap.epoch_no = _epoch_no - ORDER BY - ap.epoch_no DESC); + ap.epoch_no, ap.utxo::text, + ap.treasury::text, + ap.rewards::text, + (ap.treasury + ap.rewards + ap.utxo + ap.deposits + ap.fees)::text AS supply, + ap.reserves::text + FROM + public.ada_pots AS ap + WHERE + ap.epoch_no = _epoch_no + ORDER BY + ap.epoch_no DESC); END IF; END; $$; -COMMENT ON FUNCTION grest.totals IS 'Get the circulating utxo, treasury, rewards, supply and reserves in lovelace for specified epoch, all epochs if empty'; - +COMMENT ON FUNCTION grest.totals IS 'Get the circulating utxo, treasury, rewards, supply and reserves in lovelace for specified epoch, all epochs if empty'; -- noqa: LT01 diff --git a/files/grest/rpc/01_cached_tables/active_stake_cache.sql b/files/grest/rpc/01_cached_tables/active_stake_cache.sql index 55d71b15..4672c859 100644 --- a/files/grest/rpc/01_cached_tables/active_stake_cache.sql +++ b/files/grest/rpc/01_cached_tables/active_stake_cache.sql @@ -1,182 +1,133 @@ -CREATE TABLE IF NOT EXISTS GREST.POOL_ACTIVE_STAKE_CACHE ( - POOL_ID varchar NOT NULL, - EPOCH_NO bigint NOT NULL, - AMOUNT LOVELACE NOT NULL, - PRIMARY KEY (POOL_ID, EPOCH_NO) +CREATE TABLE IF NOT EXISTS grest.pool_active_stake_cache ( + pool_id varchar NOT NULL, + epoch_no bigint NOT NULL, + amount lovelace NOT NULL, + PRIMARY KEY (pool_id, epoch_no) ); -CREATE TABLE IF NOT EXISTS GREST.EPOCH_ACTIVE_STAKE_CACHE ( - EPOCH_NO bigint NOT NULL, - AMOUNT LOVELACE NOT NULL, - PRIMARY KEY (EPOCH_NO) +CREATE TABLE IF NOT EXISTS grest.epoch_active_stake_cache ( + epoch_no bigint NOT NULL, + amount lovelace NOT NULL, + PRIMARY KEY (epoch_no) ); -CREATE TABLE IF NOT EXISTS GREST.ACCOUNT_ACTIVE_STAKE_CACHE ( - STAKE_ADDRESS varchar NOT NULL, - POOL_ID varchar NOT NULL, - EPOCH_NO bigint NOT NULL, - AMOUNT LOVELACE NOT NULL, - PRIMARY KEY (STAKE_ADDRESS, POOL_ID, EPOCH_NO) +CREATE TABLE IF NOT EXISTS grest.account_active_stake_cache ( + stake_address varchar NOT NULL, + pool_id varchar NOT NULL, + epoch_no bigint NOT NULL, + amount lovelace NOT NULL, + PRIMARY KEY (stake_address, pool_id, epoch_no) ); -CREATE FUNCTION grest.active_stake_cache_update_check () - RETURNS BOOLEAN - LANGUAGE plpgsql - AS -$$ - DECLARE +CREATE OR REPLACE FUNCTION grest.active_stake_cache_update_check() +RETURNS boolean +LANGUAGE plpgsql +AS $$ +DECLARE _current_epoch_no integer; _last_active_stake_validated_epoch text; - BEGIN - - -- Get Last Active Stake Validated Epoch - SELECT last_value - INTO _last_active_stake_validated_epoch - FROM - grest.control_table - WHERE - key = 'last_active_stake_validated_epoch'; - - -- Get Current Epoch - SELECT MAX(NO) - INTO _current_epoch_no - FROM epoch; - - RAISE NOTICE 'Current epoch: %', - _current_epoch_no; - RAISE NOTICE 'Last active stake validated epoch: %', - _last_active_stake_validated_epoch; - - IF - _current_epoch_no > COALESCE(_last_active_stake_validated_epoch::integer, 0) - THEN - RETURN TRUE; - END IF; - - RETURN FALSE; - END; +BEGIN + -- Get Last Active Stake Validated Epoch + SELECT last_value INTO _last_active_stake_validated_epoch + FROM grest.control_table + WHERE key = 'last_active_stake_validated_epoch'; + -- Get Current Epoch + SELECT MAX(no) INTO _current_epoch_no + FROM epoch; + RAISE NOTICE 'Current epoch: %', _current_epoch_no; + RAISE NOTICE 'Last active stake validated epoch: %', _last_active_stake_validated_epoch; + IF _current_epoch_no > COALESCE(_last_active_stake_validated_epoch::integer, 0) THEN + RETURN TRUE; + END IF; + RETURN FALSE; +END; $$; -COMMENT ON FUNCTION grest.active_stake_cache_update_check - IS 'Internal function to determine whether active stake cache should be updated'; +COMMENT ON FUNCTION grest.active_stake_cache_update_check IS 'Internal function to determine whether active stake cache should be updated'; -- noqa: LT01 -CREATE FUNCTION grest.active_stake_cache_update (_epoch_no integer) - RETURNS VOID - LANGUAGE plpgsql - AS -$$ - DECLARE +CREATE OR REPLACE FUNCTION grest.active_stake_cache_update(_epoch_no integer) +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE _last_active_stake_validated_epoch integer; _last_account_active_stake_cache_epoch_no integer; - BEGIN - - /* CHECK PREVIOUS QUERY FINISHED RUNNING */ +BEGIN + -- CHECK PREVIOUS QUERY FINISHED RUNNING IF ( - SELECT - COUNT(pid) > 1 - FROM - pg_stat_activity - WHERE - state = 'active' - AND - query ILIKE '%grest.active_stake_cache_update(%' - AND - datname = ( - SELECT - current_database() - ) - ) THEN - RAISE EXCEPTION + SELECT COUNT(pid) > 1 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%grest.active_stake_cache_update(%' + AND datname = (SELECT current_database()) + ) THEN + RAISE EXCEPTION 'Previous query still running but should have completed! Exiting...'; END IF; - - /* GET PREVIOUS RUN's EPOCH_NO */ - SELECT - COALESCE(last_value::integer, 0) - INTO - _last_active_stake_validated_epoch - FROM - GREST.CONTROL_TABLE - WHERE key = 'last_active_stake_validated_epoch'; - - /* POOL ACTIVE STAKE CACHE */ - INSERT INTO GREST.POOL_ACTIVE_STAKE_CACHE + -- GET PREVIOUS RUN's epoch_no + SELECT COALESCE( + (SELECT last_value::integer + FROM grest.control_table + WHERE key = 'last_active_stake_validated_epoch'), 0) INTO _last_active_stake_validated_epoch; + -- POOL ACTIVE STAKE CACHE + INSERT INTO grest.pool_active_stake_cache SELECT - POOL_HASH.VIEW AS POOL_ID, - EPOCH_STAKE.EPOCH_NO, - SUM(EPOCH_STAKE.AMOUNT) AS AMOUNT - FROM - PUBLIC.EPOCH_STAKE - INNER JOIN PUBLIC.POOL_HASH ON POOL_HASH.ID = EPOCH_STAKE.POOL_ID - WHERE - EPOCH_STAKE.EPOCH_NO >= _last_active_stake_validated_epoch - AND - EPOCH_STAKE.EPOCH_NO <= _epoch_no + pool_hash.view AS pool_id, + epoch_stake.epoch_no, + SUM(epoch_stake.amount) AS amount + FROM public.epoch_stake + INNER JOIN public.pool_hash ON pool_hash.id = epoch_stake.pool_id + WHERE epoch_stake.epoch_no >= _last_active_stake_validated_epoch + AND epoch_stake.epoch_no <= _epoch_no GROUP BY - POOL_HASH.VIEW, - EPOCH_STAKE.EPOCH_NO + pool_hash.view, + epoch_stake.epoch_no ON CONFLICT ( - POOL_ID, - EPOCH_NO + pool_id, + epoch_no ) DO UPDATE - SET AMOUNT = EXCLUDED.AMOUNT - WHERE POOL_ACTIVE_STAKE_CACHE.AMOUNT IS DISTINCT FROM EXCLUDED.AMOUNT; - - /* EPOCH ACTIVE STAKE CACHE */ - INSERT INTO GREST.EPOCH_ACTIVE_STAKE_CACHE + SET amount = excluded.amount + WHERE pool_active_stake_cache.amount IS DISTINCT FROM excluded.amount; + -- EPOCH ACTIVE STAKE CACHE + INSERT INTO grest.epoch_active_stake_cache SELECT - EPOCH_STAKE.EPOCH_NO, - SUM(EPOCH_STAKE.AMOUNT) AS AMOUNT - FROM - PUBLIC.EPOCH_STAKE - WHERE - EPOCH_STAKE.EPOCH_NO >= _last_active_stake_validated_epoch - AND - EPOCH_STAKE.EPOCH_NO <= _epoch_no - GROUP BY - EPOCH_STAKE.EPOCH_NO - ON CONFLICT ( - EPOCH_NO - ) DO UPDATE - SET AMOUNT = EXCLUDED.AMOUNT - WHERE EPOCH_ACTIVE_STAKE_CACHE.AMOUNT IS DISTINCT FROM EXCLUDED.AMOUNT; - - /* ACCOUNT ACTIVE STAKE CACHE */ - SELECT - COALESCE(MAX(epoch_no), (_epoch_no - 4) ) - INTO _last_account_active_stake_cache_epoch_no - FROM - GREST.ACCOUNT_ACTIVE_STAKE_CACHE; - - INSERT INTO GREST.ACCOUNT_ACTIVE_STAKE_CACHE + epoch_stake.epoch_no, + SUM(epoch_stake.amount) AS amount + FROM public.epoch_stake + WHERE epoch_stake.epoch_no >= _last_active_stake_validated_epoch + AND epoch_stake.epoch_no <= _epoch_no + GROUP BY epoch_stake.epoch_no + ON CONFLICT (epoch_no) DO UPDATE + SET amount = excluded.amount + WHERE epoch_active_stake_cache.amount IS DISTINCT FROM excluded.amount; + -- ACCOUNT ACTIVE STAKE CACHE + SELECT COALESCE(MAX(epoch_no), (_epoch_no - 4)) INTO _last_account_active_stake_cache_epoch_no + FROM grest.account_active_stake_cache; + + INSERT INTO grest.account_active_stake_cache SELECT - STAKE_ADDRESS.VIEW AS STAKE_ADDRESS, - POOL_HASH.VIEW AS POOL_ID, - EPOCH_STAKE.EPOCH_NO AS EPOCH_NO, - SUM(EPOCH_STAKE.AMOUNT) AS AMOUNT - FROM - PUBLIC.EPOCH_STAKE - INNER JOIN PUBLIC.POOL_HASH ON POOL_HASH.ID = EPOCH_STAKE.POOL_ID - INNER JOIN PUBLIC.STAKE_ADDRESS ON STAKE_ADDRESS.ID = EPOCH_STAKE.ADDR_ID - WHERE - EPOCH_STAKE.EPOCH_NO > _last_account_active_stake_cache_epoch_no - AND - EPOCH_STAKE.EPOCH_NO <= _epoch_no + stake_address.view AS stake_address, + pool_hash.view AS pool_id, + epoch_stake.epoch_no AS epoch_no, + SUM(epoch_stake.amount) AS amount + FROM public.epoch_stake + INNER JOIN public.pool_hash ON pool_hash.id = epoch_stake.pool_id + INNER JOIN public.stake_address ON stake_address.id = epoch_stake.addr_id + WHERE epoch_stake.epoch_no > _last_account_active_stake_cache_epoch_no + AND epoch_stake.epoch_no <= _epoch_no GROUP BY - STAKE_ADDRESS.ID, - POOL_HASH.ID, - EPOCH_STAKE.EPOCH_NO + stake_address.id, + pool_hash.id, + epoch_stake.epoch_no ON CONFLICT ( - STAKE_ADDRESS, - POOL_ID, - EPOCH_NO + stake_address, + pool_id, + epoch_no ) DO UPDATE - SET AMOUNT = EXCLUDED.AMOUNT; - - DELETE FROM GREST.ACCOUNT_ACTIVE_STAKE_CACHE - WHERE EPOCH_NO <= (_epoch_no - 4); - - /* CONTROL TABLE ENTRY */ + SET amount = excluded.amount; + DELETE FROM grest.account_active_stake_cache + WHERE epoch_no <= (_epoch_no - 4); + -- CONTROL TABLE ENTRY PERFORM grest.update_control_table( 'last_active_stake_validated_epoch', _epoch_no::text @@ -184,5 +135,4 @@ $$ END; $$; -COMMENT ON FUNCTION grest.active_stake_cache_update - IS 'Internal function to update active stake cache (epoch, pool, and account tables).'; +COMMENT ON FUNCTION grest.active_stake_cache_update IS 'Internal function to update active stake cache (epoch, pool, and account tables).'; -- noqa: LT01 diff --git a/files/grest/rpc/01_cached_tables/asset_info_cache.sql b/files/grest/rpc/01_cached_tables/asset_info_cache.sql index 7fa2e119..9dd11d16 100644 --- a/files/grest/rpc/01_cached_tables/asset_info_cache.sql +++ b/files/grest/rpc/01_cached_tables/asset_info_cache.sql @@ -6,18 +6,18 @@ CREATE TABLE IF NOT EXISTS grest.asset_info_cache ( mint_cnt bigint, burn_cnt bigint, first_mint_tx_id bigint, - first_mint_keys text[], + first_mint_keys text [], last_mint_tx_id bigint, - last_mint_keys text[] + last_mint_keys text [] ); CREATE INDEX IF NOT EXISTS idx_first_mint_tx_id ON grest.asset_info_cache (first_mint_tx_id); CREATE INDEX IF NOT EXISTS idx_last_mint_tx_id ON grest.asset_info_cache (last_mint_tx_id); -CREATE OR REPLACE FUNCTION grest.asset_info_cache_update () - RETURNS void - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_info_cache_update() +RETURNS void +LANGUAGE plpgsql +AS $$ DECLARE _lastest_tx_id bigint; _asset_info_cache_last_tx_id bigint; @@ -25,40 +25,30 @@ DECLARE BEGIN -- Check previous cache update completed before running IF ( - SELECT - COUNT(pid) > 1 - FROM - pg_stat_activity - WHERE - state = 'active' AND query ILIKE '%grest.asset_info_cache_update%' + SELECT COUNT(pid) > 1 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%grest.asset_info_cache_update%' AND datname = (SELECT current_database()) ) THEN RAISE EXCEPTION 'Previous asset_info_cache_update query still running but should have completed! Exiting...'; END IF; - SELECT - MAX(id) INTO _lastest_tx_id - FROM - public.tx; + SELECT MAX(id) INTO _lastest_tx_id + FROM public.tx; - SELECT - COALESCE(last_value::bigint,1000) - 1000 INTO _asset_info_cache_last_tx_id - FROM - grest.control_table - WHERE - key = 'asset_info_cache_last_tx_id'; + SELECT COALESCE(last_value::bigint,1000) - 1000 INTO _asset_info_cache_last_tx_id + FROM grest.control_table + WHERE key = 'asset_info_cache_last_tx_id'; IF _asset_info_cache_last_tx_id IS NULL THEN RAISE NOTICE 'Asset info cache table is empty, deleting all previous cache records and starting initial population...'; TRUNCATE TABLE grest.asset_info_cache; ELSE - RAISE NOTICE 'Updating asset info based on data from transaction id in range % - % ...', _asset_info_cache_last_tx_id, _lastest_tx_id; - SELECT - ARRAY_AGG(DISTINCT ident) INTO _asset_id_list - FROM - ma_tx_mint - WHERE - tx_id > _asset_info_cache_last_tx_id; + RAISE NOTICE 'Updating asset info based ON data FROM transaction id in range % - % ...', _asset_info_cache_last_tx_id, _lastest_tx_id; + SELECT ARRAY_AGG(DISTINCT ident) INTO _asset_id_list + FROM ma_tx_mint + WHERE tx_id > _asset_info_cache_last_tx_id; END IF; WITH @@ -68,17 +58,15 @@ BEGIN mtm.ident, MIN(mtm.tx_id) AS first_mint_tx_id, MAX(mtm.tx_id) AS last_mint_tx_id - FROM - ma_tx_mint mtm - INNER JOIN tx_metadata tm ON tm.tx_id = mtm.tx_id + FROM ma_tx_mint AS mtm + INNER JOIN tx_metadata AS tm ON tm.tx_id = mtm.tx_id WHERE CASE WHEN _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL THEN - mtm.ident = any(_asset_id_list) + mtm.ident = ANY(_asset_id_list) AND mtm.tx_id > _asset_info_cache_last_tx_id ELSE TRUE END - AND mtm.quantity > 0 AND tm.json IS NOT NULL GROUP BY mtm.ident ), @@ -88,21 +76,19 @@ BEGIN mtm.ident, MIN(mtm.tx_id) AS first_mint_tx_id, MAX(mtm.tx_id) AS last_mint_tx_id - FROM - ma_tx_mint mtm - LEFT JOIN tx_mint_meta ON tx_mint_meta.ident = mtm.ident + FROM ma_tx_mint AS mtm + LEFT JOIN tx_mint_meta ON tx_mint_meta.ident = mtm.ident WHERE CASE WHEN _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL THEN - mtm.ident = any(_asset_id_list) + mtm.ident = ANY(_asset_id_list) AND mtm.tx_id > _asset_info_cache_last_tx_id ELSE TRUE END AND tx_mint_meta IS NULL - AND mtm.quantity > 0 GROUP BY mtm.ident ), - + tx_meta AS ( SELECT tmm.ident, @@ -110,9 +96,8 @@ BEGIN ARRAY_AGG(tm.key) FILTER(WHERE tm.tx_id = tmm.first_mint_tx_id) AS first_mint_keys, tmm.last_mint_tx_id, ARRAY_AGG(tm.key) FILTER(WHERE tm.tx_id = tmm.last_mint_tx_id) AS last_mint_keys - FROM - tx_mint_meta tmm - INNER JOIN tx_metadata tm ON tm.tx_id = tmm.first_mint_tx_id OR tm.tx_id = tmm.last_mint_tx_id + FROM tx_mint_meta AS tmm + INNER JOIN tx_metadata AS tm ON tm.tx_id = tmm.first_mint_tx_id OR tm.tx_id = tmm.last_mint_tx_id GROUP BY tmm.ident, tmm.first_mint_tx_id, tmm.last_mint_tx_id -- UNION ALL @@ -123,8 +108,7 @@ BEGIN '{}', tx_mint_nometa.last_mint_tx_id, '{}' - FROM - tx_mint_nometa + FROM tx_mint_nometa ) INSERT INTO grest.asset_info_cache @@ -140,28 +124,28 @@ BEGIN tm.last_mint_tx_id, tm.last_mint_keys FROM - multi_asset ma - INNER JOIN ma_tx_mint mtm ON mtm.ident = ma.id + multi_asset AS ma + INNER JOIN ma_tx_mint AS mtm ON mtm.ident = ma.id INNER JOIN tx ON tx.id = mtm.tx_id - INNER JOIN block b ON b.id = tx.block_id - INNER JOIN tx_meta tm ON tm.ident = ma.id - LEFT JOIN grest.asset_registry_cache arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = encode(ma.name,'hex') + INNER JOIN block AS b ON b.id = tx.block_id + INNER JOIN tx_meta AS tm ON tm.ident = ma.id + LEFT JOIN grest.asset_registry_cache AS arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = encode(ma.name,'hex') WHERE CASE WHEN _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL THEN - mtm.ident = any(_asset_id_list) + mtm.ident = ANY(_asset_id_list) ELSE TRUE END GROUP BY ma.id, arc.decimals, tm.first_mint_tx_id, tm.first_mint_keys, tm.last_mint_tx_id, tm.last_mint_keys ON CONFLICT (asset_id) DO UPDATE SET - creation_time = excluded.creation_time, - total_supply = excluded.total_supply, - decimals = excluded.decimals, - mint_cnt = excluded.mint_cnt, - burn_cnt = excluded.burn_cnt, - last_mint_tx_id = excluded.last_mint_tx_id, - last_mint_keys = excluded.last_mint_keys; + creation_time = excluded.creation_time, + total_supply = excluded.total_supply, + decimals = excluded.decimals, + mint_cnt = excluded.mint_cnt, + burn_cnt = excluded.burn_cnt, + last_mint_tx_id = excluded.last_mint_tx_id, + last_mint_keys = excluded.last_mint_keys; IF _asset_info_cache_last_tx_id IS NOT NULL AND _asset_id_list IS NOT NULL THEN RAISE NOTICE '% assets added or updated', ARRAY_LENGTH(_asset_id_list, 1); diff --git a/files/grest/rpc/01_cached_tables/asset_registry_cache.sql b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql index 407563f2..7d1ce37f 100644 --- a/files/grest/rpc/01_cached_tables/asset_registry_cache.sql +++ b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql @@ -1,59 +1,59 @@ DROP TABLE IF EXISTS grest.asset_registry_cache CASCADE; CREATE TABLE grest.asset_registry_cache ( - asset_policy text NOT NULL, - asset_name text NOT NULL, - name text NOT NULL, - description text NOT NULL, - ticker text, - url text, - logo text, - decimals integer + asset_policy text NOT NULL, + asset_name text NOT NULL, + name text NOT NULL, + description text NOT NULL, + ticker text, + url text, + logo text, + decimals integer ); CREATE UNIQUE INDEX IF NOT EXISTS idx_asset ON grest.asset_registry_cache (asset_policy, asset_name); -CREATE FUNCTION grest.asset_registry_cache_update ( - _asset_policy text, - _asset_name text, - _name text, - _description text, - _ticker text DEFAULT NULL, - _url text DEFAULT NULL, - _logo text DEFAULT NULL, - _decimals word31type DEFAULT 0 - ) - RETURNS void - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_registry_cache_update( + _asset_policy text, + _asset_name text, + _name text, + _description text, + _ticker text DEFAULT NULL, + _url text DEFAULT NULL, + _logo text DEFAULT NULL, + _decimals word31type DEFAULT 0 +) +RETURNS void +LANGUAGE plpgsql +AS $$ BEGIN - INSERT INTO grest.asset_registry_cache ( - asset_policy, - asset_name, - name, - description, - ticker, - url, - logo, - decimals - ) - VALUES( - _asset_policy, - _asset_name, - _name, - _description, - _ticker, - _url, - _logo, - _decimals - ) - ON CONFLICT (asset_policy, asset_name) - DO UPDATE SET - name = _name, - description = _description, - ticker = _ticker, - url = _url, - logo = _logo, - decimals = _decimals; + INSERT INTO grest.asset_registry_cache ( + asset_policy, + asset_name, + name, + description, + ticker, + url, + logo, + decimals + ) + VALUES( + _asset_policy, + _asset_name, + _name, + _description, + _ticker, + _url, + _logo, + _decimals + ) + ON CONFLICT (asset_policy, asset_name) + DO UPDATE SET + name = _name, + description = _description, + ticker = _ticker, + url = _url, + logo = _logo, + decimals = _decimals; END; $$; diff --git a/files/grest/rpc/01_cached_tables/epoch_info_cache.sql b/files/grest/rpc/01_cached_tables/epoch_info_cache.sql index fca31a91..c6a2fc00 100644 --- a/files/grest/rpc/01_cached_tables/epoch_info_cache.sql +++ b/files/grest/rpc/01_cached_tables/epoch_info_cache.sql @@ -44,51 +44,38 @@ CREATE TABLE IF NOT EXISTS grest.epoch_info_cache ( COMMENT ON TABLE grest.epoch_info_cache IS 'Contains detailed info for epochs including protocol parameters'; -CREATE FUNCTION grest.EPOCH_INFO_CACHE_UPDATE ( - _epoch_no_to_insert_from bigint default NULL - ) - RETURNS void - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.epoch_info_cache_update( + _epoch_no_to_insert_from bigint DEFAULT NULL +) +RETURNS void +LANGUAGE plpgsql +AS $$ DECLARE _curr_epoch bigint; _latest_epoch_no_in_cache bigint; BEGIN -- Check previous cache update completed before running IF ( - SELECT - COUNT(pid) > 1 - FROM - pg_stat_activity - WHERE - state = 'active' AND query ILIKE '%GREST.EPOCH_INFO_CACHE_UPDATE%' + SELECT COUNT(pid) > 1 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%grest.epoch_info_cache_update%' AND datname = (SELECT current_database()) ) THEN - RAISE EXCEPTION 'Previous EPOCH_INFO_CACHE_UPDATE query still running but should have completed! Exiting...'; + RAISE EXCEPTION 'Previous epoch_info_cache_update query still running but should have completed! Exiting...'; END IF; - -- GREST control table entry - PERFORM grest.update_control_table( - 'epoch_info_cache_last_updated', - (now() at time zone 'utc')::text - ); - - SELECT - MAX(no) INTO _curr_epoch - FROM - public.epoch; + SELECT MAX(no) INTO _curr_epoch + FROM public.epoch; IF _epoch_no_to_insert_from IS NULL THEN - SELECT - COALESCE(MAX(epoch_no), 0) INTO _latest_epoch_no_in_cache - FROM - grest.epoch_info_cache - WHERE - i_first_block_time IS NOT NULL; + SELECT COALESCE(MAX(epoch_no), 0) INTO _latest_epoch_no_in_cache + FROM grest.epoch_info_cache + WHERE i_first_block_time IS NOT NULL; IF _latest_epoch_no_in_cache = 0 THEN RAISE NOTICE 'Epoch info cache table is empty, starting initial population...'; - PERFORM grest.EPOCH_INFO_CACHE_UPDATE (0); + PERFORM grest.epoch_info_cache_update(0); RETURN; END IF; @@ -96,7 +83,7 @@ BEGIN IF _curr_epoch = _latest_epoch_no_in_cache THEN RAISE NOTICE 'Updating latest epoch info in cache...'; - PERFORM grest.UPDATE_LATEST_EPOCH_INFO_CACHE(_curr_epoch, _latest_epoch_no_in_cache); + PERFORM grest.update_latest_epoch_info_cache(_curr_epoch, _latest_epoch_no_in_cache); RETURN; END IF; @@ -107,16 +94,16 @@ BEGIN RAISE NOTICE 'Updating cache with new epoch(s) data...'; -- We need to update last epoch one last time before going to new one - PERFORM grest.UPDATE_LATEST_EPOCH_INFO_CACHE(_curr_epoch, _latest_epoch_no_in_cache); + PERFORM grest.update_latest_epoch_info_cache(_curr_epoch, _latest_epoch_no_in_cache); -- Populate rewards data for epoch n - 2 - PERFORM grest.UPDATE_TOTAL_REWARDS_EPOCH_INFO_CACHE(_latest_epoch_no_in_cache - 1); + PERFORM grest.update_total_rewards_epoch_info_cache(_latest_epoch_no_in_cache - 1); -- Continue new epoch data insert _epoch_no_to_insert_from := _latest_epoch_no_in_cache + 1; END IF; - RAISE NOTICE 'Deleting cache records from epoch % onwards...', _epoch_no_to_insert_from; + RAISE NOTICE 'Deleting cache records FROM epoch % onwards...', _epoch_no_to_insert_from; DELETE FROM grest.epoch_info_cache - WHERE epoch_no >= _epoch_no_to_insert_from; + WHERE epoch_no >= _epoch_no_to_insert_from; INSERT INTO grest.epoch_info_cache SELECT DISTINCT ON (b.time) @@ -125,8 +112,8 @@ BEGIN e.fees AS i_fees, e.tx_count AS i_tx_count, e.blk_count AS i_blk_count, - EXTRACT(epoch from e.start_time) AS i_first_block_time, - EXTRACT(epoch from e.end_time) AS i_last_block_time, + EXTRACT(EPOCH FROM e.start_time) AS i_first_block_time, + EXTRACT(EPOCH FROM e.end_time) AS i_last_block_time, CASE -- populated in epoch n + 2 WHEN e.no <= _curr_epoch - 2 THEN reward_pot.amount ELSE NULL @@ -167,76 +154,70 @@ BEGIN ep.collateral_percent AS p_collateral_percent, ep.max_collateral_inputs AS p_max_collateral_inputs, ep.coins_per_utxo_size AS p_coins_per_utxo_size - FROM - epoch e - LEFT JOIN epoch_param ep ON ep.epoch_no = e.no - LEFT JOIN cost_model cm ON cm.id = ep.cost_model_id - INNER JOIN block b ON b.time = e.start_time - LEFT JOIN LATERAL ( + FROM epoch AS e + LEFT JOIN epoch_param AS ep ON ep.epoch_no = e.no + LEFT JOIN cost_model AS cm ON cm.id = ep.cost_model_id + INNER JOIN block AS b ON b.time = e.start_time + LEFT JOIN LATERAL ( SELECT e.no, - SUM(r.amount) as amount - FROM - reward r - WHERE - r.earned_epoch = e.no + SUM(r.amount) AS amount + FROM reward AS r + WHERE r.earned_epoch = e.no GROUP BY e.no - ) reward_pot ON TRUE - LEFT JOIN LATERAL ( - SELECT - MAX(tx.id) AS tx_id - FROM - block b - INNER JOIN tx ON tx.block_id = b.id - WHERE - b.epoch_no <= e.no + ) AS reward_pot ON TRUE + LEFT JOIN LATERAL ( + SELECT MAX(tx.id) AS tx_id + FROM block AS b + INNER JOIN tx ON tx.block_id = b.id + WHERE b.epoch_no <= e.no AND b.block_no IS NOT NULL AND b.tx_count != 0 - ) last_tx ON TRUE - WHERE - e.no >= _epoch_no_to_insert_from + ) AS last_tx ON TRUE + WHERE e.no >= _epoch_no_to_insert_from ORDER BY b.time ASC, b.id ASC, e.no ASC; + + -- GREST control table entry + PERFORM grest.update_control_table( + 'epoch_info_cache_last_updated', + (now() at time zone 'utc')::text + ); + END; $$; -- Helper function for updating current epoch data -CREATE FUNCTION grest.UPDATE_LATEST_EPOCH_INFO_CACHE (_curr_epoch bigint, _epoch_no_to_update bigint) - RETURNS void - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.update_latest_epoch_info_cache(_curr_epoch bigint, _epoch_no_to_update bigint) +RETURNS void +LANGUAGE plpgsql +AS $$ BEGIN -- only update last tx id in case of new epoch IF _curr_epoch <> _epoch_no_to_update THEN - UPDATE - grest.epoch_info_cache - SET - i_last_tx_id = last_tx.tx_id + UPDATE grest.epoch_info_cache + SET i_last_tx_id = last_tx.tx_id FROM ( - SELECT - MAX(tx.id) AS tx_id - FROM - block b - INNER JOIN tx ON tx.block_id = b.id - WHERE - b.epoch_no <= _epoch_no_to_update + SELECT MAX(tx.id) AS tx_id + FROM block AS b + INNER JOIN tx ON tx.block_id = b.id + WHERE b.epoch_no <= _epoch_no_to_update AND b.block_no IS NOT NULL AND b.tx_count != 0 - ) last_tx + ) AS last_tx WHERE epoch_no = _epoch_no_to_update; END IF; - UPDATE - grest.epoch_info_cache + UPDATE grest.epoch_info_cache SET i_out_sum = update_table.out_sum, i_fees = update_table.fees, i_tx_count = update_table.tx_count, i_blk_count = update_table.blk_count, - i_last_block_time = EXTRACT(epoch from update_table.end_time) + i_last_block_time = EXTRACT(EPOCH FROM update_table.end_time) FROM ( SELECT e.out_sum, @@ -244,24 +225,20 @@ BEGIN e.tx_count, e.blk_count, e.end_time - FROM - epoch e - WHERE - e.no = _epoch_no_to_update - ) update_table - WHERE - epoch_no = _epoch_no_to_update; + FROM epoch AS e + WHERE e.no = _epoch_no_to_update + ) AS update_table + WHERE epoch_no = _epoch_no_to_update; END; $$; -- Helper function for updating epoch total rewards (epoch n - 2) -CREATE FUNCTION grest.UPDATE_TOTAL_REWARDS_EPOCH_INFO_CACHE (_epoch_no_to_update bigint) - RETURNS void - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.update_total_rewards_epoch_info_cache(_epoch_no_to_update bigint) +RETURNS void +LANGUAGE plpgsql +AS $$ BEGIN - UPDATE - grest.epoch_info_cache + UPDATE grest.epoch_info_cache SET i_total_rewards = update_t.amount, i_avg_blk_reward = update_t.avg_blk_reward @@ -273,18 +250,13 @@ BEGIN SELECT r.earned_epoch, SUM(r.amount) AS amount - FROM - reward r - WHERE - r.earned_epoch = _epoch_no_to_update - GROUP BY - r.earned_epoch - ) reward_pot - INNER JOIN epoch e ON reward_pot.earned_epoch = e.no + FROM reward AS r + WHERE r.earned_epoch = _epoch_no_to_update + GROUP BY r.earned_epoch + ) AS reward_pot + INNER JOIN epoch AS e ON reward_pot.earned_epoch = e.no AND e.no = _epoch_no_to_update - ) update_t - WHERE - epoch_no = _epoch_no_to_update; + ) AS update_t + WHERE epoch_no = _epoch_no_to_update; END; $$; - diff --git a/files/grest/rpc/01_cached_tables/pool_history_cache.sql b/files/grest/rpc/01_cached_tables/pool_history_cache.sql index 3a5bea3c..9644e847 100644 --- a/files/grest/rpc/01_cached_tables/pool_history_cache.sql +++ b/files/grest/rpc/01_cached_tables/pool_history_cache.sql @@ -1,4 +1,4 @@ -drop table if exists grest.pool_history_cache; +DROP TABLE IF EXISTS grest.pool_history_cache; CREATE TABLE grest.pool_history_cache ( pool_id varchar, @@ -17,256 +17,261 @@ CREATE TABLE grest.pool_history_cache ( PRIMARY KEY (pool_id, epoch_no) ); -COMMENT ON TABLE grest.pool_history_cache IS 'A history of pool performance including blocks, delegators, active stake, fees and rewards'; +COMMENT ON TABLE grest.pool_history_cache IS 'A history of pool performance including blocks, delegators, active stake, fees AND rewards'; -CREATE OR REPLACE grest.pool_history_cache_update (_epoch_no_to_insert_from bigint default NULL) - returns void - language plpgsql - as $$ -declare +CREATE OR REPLACE FUNCTION grest.pool_history_cache_update(_epoch_no_to_insert_from bigint DEFAULT NULL) +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE _curr_epoch bigint; _latest_epoch_no_in_cache bigint; -begin +BEGIN IF ( - SELECT - COUNT(pid) > 1 - FROM - pg_stat_activity - WHERE - state = 'active' AND query ILIKE '%GREST.pool_history_cache_update%' + SELECT COUNT(pid) > 1 + FROM pg_stat_activity + WHERE state = 'active' AND query ILIKE '%grest.pool_history_cache_update%' AND datname = (SELECT current_database()) ) THEN - RAISE EXCEPTION 'Previous pool_history_cache_update query still running but should have completed! Exiting...'; - END IF; + RAISE EXCEPTION 'Previous pool_history_cache_update query still running but should have completed! Exiting...'; + END IF; - INSERT INTO GREST.CONTROL_TABLE (key, last_value) - values('pool_history_cache_last_updated', now() at time zone 'utc') - ON CONFLICT (key) - DO UPDATE SET last_value = now() at time zone 'utc'; + IF ( + SELECT COUNT(key) != 1 + FROM GREST.CONTROL_TABLE + WHERE key = 'epoch_info_cache_last_updated' + ) THEN + RAISE EXCEPTION 'Epoch Info Cache not yet populated! Exiting...'; + END IF; - if _epoch_no_to_insert_from is null then - select - COALESCE(MAX(epoch_no), 0) into _latest_epoch_no_in_cache - from - grest.pool_history_cache; - if _latest_epoch_no_in_cache = 0 then + IF _epoch_no_to_insert_from IS NULL THEN + SELECT COALESCE(MAX(epoch_no), 0) INTO _latest_epoch_no_in_cache + FROM grest.pool_history_cache; + IF _latest_epoch_no_in_cache = 0 THEN RAISE NOTICE 'Pool history cache table is empty, starting initial population...'; PERFORM grest.pool_history_cache_update (0); - return; - end if; - select - MAX(no) into _curr_epoch - from - epoch; - -- no-op if we already have data up until second most recent epoch - if _latest_epoch_no_in_cache >= (_curr_epoch - 1) then - return; - end if; - -- if current epoch is at least 2 ahead of latest in cache, repopulate from latest in cache until current-1 + RETURN; + END IF; + SELECT MAX(no) INTO _curr_epoch + FROM epoch; + -- no-op IF we already have data up until second most recent epoch + IF _latest_epoch_no_in_cache >= (_curr_epoch - 1) THEN + INSERT INTO grest.control_table (key, last_value) + VALUES ('pool_history_cache_last_updated', NOW() AT TIME ZONE 'utc') + ON CONFLICT (key) + DO UPDATE SET last_value = NOW() AT TIME ZONE 'utc'; + RETURN; + END IF; + -- IF current epoch is at least 2 ahead of latest in cache, repopulate FROM latest in cache until current-1 _epoch_no_to_insert_from := _latest_epoch_no_in_cache; - end if; - -- purge the data for the given epoch range, in theory should do nothing if invoked only at start of new epoch - delete from grest.pool_history_cache - where epoch_no >= _epoch_no_to_insert_from; - insert into grest.pool_history_cache ( with blockcounts as ( - select + END IF; + -- purge the data for the given epoch range, in theory should do nothing IF invoked only at start of new epoch + DELETE FROM grest.pool_history_cache + WHERE epoch_no >= _epoch_no_to_insert_from; + INSERT INTO grest.pool_history_cache ( + WITH + blockcounts AS ( + SELECT sl.pool_hash_id, b.epoch_no, - COUNT(*) as block_cnt - from - block b, - slot_leader sl - where - b.slot_leader_id = sl.id - and epoch_no >= _epoch_no_to_insert_from - group by + COUNT(*) AS block_cnt + FROM + block AS b, + slot_leader AS sl + WHERE b.slot_leader_id = sl.id + AND b.epoch_no >= _epoch_no_to_insert_from + GROUP BY sl.pool_hash_id, - b.epoch_no), - leadertotals as ( - select - pool_id, - earned_epoch, - COALESCE(SUM(amount), 0) as leadertotal - from - reward r - where - r.type = 'leader' - and earned_epoch >= _epoch_no_to_insert_from - group by - pool_id, - earned_epoch), - membertotals as ( - select - pool_id, - earned_epoch, - COALESCE(SUM(amount), 0) as memtotal - from - reward r - where - r.type = 'member' - and earned_epoch >= _epoch_no_to_insert_from - group by - pool_id, - earned_epoch), - activeandfees as ( - select - pool_id, - epoch_no, - amount as active_stake, - ( - select - margin - from - pool_update - where - id = ( - select - MAX(pup2.id) - from - pool_hash ph, - pool_update pup2 - where - pup2.hash_id = ph.id - and ph.view = act.pool_id - and pup2.active_epoch_no <= act.epoch_no)) pool_fee_variable, - ( - select - fixed_cost - from - pool_update - where - id = ( - select - MAX(pup2.id) - from - pool_update pup2, - pool_hash ph - where - ph.view = act.pool_id - and pup2.hash_id = ph.id - and pup2.active_epoch_no <= act.epoch_no)) pool_fee_fixed, - (amount / ( - select - NULLIF (amount, 0) - from - grest.EPOCH_ACTIVE_STAKE_CACHE epochActiveStakeCache - where - epochActiveStakeCache.epoch_no = act.epoch_no)) * 100 active_stake_pct, - ROUND((amount / ( - select - supply::bigint / ( - select - p_optimal_pool_count from grest.epoch_info_cache - where - epoch_no = act.epoch_no) - from grest.totals (act.epoch_no)) * 100), 2) saturation_pct - from - grest.pool_active_stake_cache act - where - epoch_no >= _epoch_no_to_insert_from - -- TODO: revisit later: currently ignore latest epoch as active stake might not be populated for it yet - and epoch_no < ( - select - MAX(e."no") - from - epoch e)), - delegators as ( - select - pool_id, - epoch_no, - COUNT(*) as delegator_cnt - from - epoch_stake es - where - epoch_no >= _epoch_no_to_insert_from - group by - pool_id, - epoch_no -) - select - ph.view as pool_id, - actf.epoch_no, - actf.active_stake, - actf.active_stake_pct, - actf.saturation_pct, - COALESCE(b.block_cnt, 0) as block_cnt, - del.delegator_cnt, - actf.pool_fee_variable, - actf.pool_fee_fixed, - -- for debugging: m.memtotal, - -- for debugging: l.leadertotal, - case COALESCE(b.block_cnt, 0) - when 0 then - 0 - else - -- special case for when reward information is not available yet - case COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) - when 0 then - null - else - case - when COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed then COALESCE(l.leadertotal, 0) - else ROUND(actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable)) - end - end - end pool_fees, - case COALESCE(b.block_cnt, 0) - when 0 then - 0 - else - -- special case for when reward information is not available yet - case COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) - when 0 then - null - else - case - when COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed then COALESCE(m.memtotal, 0) - else ROUND(COALESCE(m.memtotal, 0) + (COALESCE(l.leadertotal, 0) - (actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable)))) - end - end - end deleg_rewards, - case COALESCE(b.block_cnt, 0) - when 0 then - 0 - else - case COALESCE(m.memtotal, 0) - when 0 then - null - else - COALESCE(m.memtotal, 0) - end - end member_rewards, - case COALESCE(b.block_cnt, 0) - when 0 then - 0 - else - -- special case for when reward information is not available yet - case COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) - when 0 then - null - else - case - when COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed then - ROUND((((POW(( LEAST(( (COALESCE(m.memtotal, 0)) / (NULLIF(actf.active_stake,0)) ), 1000) + 1), 73) - 1)) * 100)::numeric, 9) - -- using LEAST as a way to prevent overflow, in case of dodgy database data (e.g. giant rewards / tiny active stake) - else ROUND((((POW(( LEAST(( ((COALESCE(m.memtotal, 0) + (COALESCE(l.leadertotal, 0) - (actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + - COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable))))) / (NULLIF(actf.active_stake,0)) ), 1000) + 1), 73) - 1)) * 100)::numeric, 9) - end - end - end epoch_ros - from - pool_hash ph - inner join activeandfees actf on actf.pool_id = ph."view" - left join blockcounts b on ph.id = b.pool_hash_id - and actf.epoch_no = b.epoch_no - left join leadertotals l on ph.id = l.pool_id - and actf.epoch_no = l.earned_epoch - left join membertotals m on ph.id = m.pool_id - and actf.epoch_no = m.earned_epoch - left join delegators del on ph.id = del.pool_id - and actf.epoch_no = del.epoch_no); -end; + b.epoch_no + ), + + leadertotals AS ( + SELECT + r.pool_id, + r.earned_epoch, + COALESCE(SUM(r.amount), 0) AS leadertotal + FROM reward AS r + WHERE r.type = 'leader' + AND r.earned_epoch >= _epoch_no_to_insert_from + GROUP BY + r.pool_id, + r.earned_epoch + ), + + membertotals AS ( + SELECT + r.pool_id, + r.earned_epoch, + COALESCE(SUM(r.amount), 0) AS memtotal + FROM reward AS r + WHERE r.type = 'member' + AND r.earned_epoch >= _epoch_no_to_insert_from + GROUP BY + r.pool_id, + r.earned_epoch + ), + + activeandfees AS ( + SELECT + act.pool_id, + act.epoch_no, + act.amount AS active_stake, + ( + SELECT margin + FROM + pool_update + WHERE + id = ( + SELECT MAX(pup2.id) + FROM + pool_hash AS ph, + pool_update AS pup2 + WHERE pup2.hash_id = ph.id + AND ph.view = act.pool_id + AND pup2.active_epoch_no <= act.epoch_no + ) + ) AS pool_fee_variable, + ( + SELECT fixed_cost + FROM + pool_update + WHERE id = ( + SELECT MAX(pup2.id) + FROM + pool_update AS pup2, + pool_hash AS ph + WHERE ph.view = act.pool_id + AND pup2.hash_id = ph.id + AND pup2.active_epoch_no <= act.epoch_no) + ) AS pool_fee_fixed, + (act.amount / ( + SELECT NULLIF(act.amount, 0) + FROM grest.epoch_active_stake_cache AS easc + WHERE easc.epoch_no = act.epoch_no + ) + ) * 100 AS active_stake_pct, + ROUND( + (act.amount / ( + SELECT supply::bigint / ( + SELECT eic.p_optimal_pool_count + FROM grest.epoch_info_cache AS eic + WHERE eic.epoch_no = act.epoch_no + ) + FROM grest.totals (act.epoch_no) + ) * 100 + ), 2 + ) AS saturation_pct + FROM grest.pool_active_stake_cache AS act + WHERE act.epoch_no >= _epoch_no_to_insert_from + -- TODO: revisit later: currently ignore latest epoch AS active stake might not be populated for it yet + AND act.epoch_no < ( + SELECT MAX(e."no") + FROM + epoch AS e + ) + ), + + delegators AS ( + SELECT + es.pool_id, + es.epoch_no, + COUNT(1) AS delegator_cnt + FROM epoch_stake AS es + WHERE es.epoch_no >= _epoch_no_to_insert_from + GROUP BY + es.pool_id, + es.epoch_no + ) + + SELECT + ph.view AS pool_id, + actf.epoch_no, + actf.active_stake, + actf.active_stake_pct, + actf.saturation_pct, + COALESCE(b.block_cnt, 0) AS block_cnt, + del.delegator_cnt, + actf.pool_fee_variable, + actf.pool_fee_fixed, + -- for debugging: m.memtotal, + -- for debugging: l.leadertotal, + CASE COALESCE(b.block_cnt, 0) + WHEN 0 THEN + 0 + ELSE + -- special CASE for WHEN reward information is not available yet + CASE COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) + WHEN 0 THEN + NULL + ELSE + CASE + WHEN COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed THEN COALESCE(l.leadertotal, 0) + ELSE ROUND(actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable)) + END + END + END AS pool_fees, + CASE COALESCE(b.block_cnt, 0) + WHEN 0 THEN + 0 + ELSE + -- special CASE for WHEN reward information is not available yet + CASE COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) + WHEN 0 THEN + NULL + ELSE + CASE + WHEN COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed THEN COALESCE(m.memtotal, 0) + ELSE ROUND(COALESCE(m.memtotal, 0) + (COALESCE(l.leadertotal, 0) - (actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable)))) + END + END + END AS deleg_rewards, + CASE COALESCE(b.block_cnt, 0) + WHEN 0 THEN + 0 + ELSE + CASE COALESCE(m.memtotal, 0) + WHEN 0 THEN + NULL + ELSE + COALESCE(m.memtotal, 0) + END + END AS member_rewards, + CASE COALESCE(b.block_cnt, 0) + WHEN 0 THEN + 0 + ELSE + -- special CASE for WHEN reward information is not available yet + CASE COALESCE(l.leadertotal, 0) + COALESCE(m.memtotal, 0) + WHEN 0 THEN + NULL + ELSE + CASE + WHEN COALESCE(l.leadertotal, 0) < actf.pool_fee_fixed THEN + ROUND((((POW((LEAST(((COALESCE(m.memtotal, 0)) / (NULLIF(actf.active_stake, 0))), 1000) + 1), 73) - 1)) * 100)::numeric, 9) + -- using LEAST AS a way to prevent overflow, in CASE of dodgy database data (e.g. giant rewards / tiny active stake) + ELSE ROUND((((POW((LEAST((((COALESCE(m.memtotal, 0) + (COALESCE(l.leadertotal, 0) - (actf.pool_fee_fixed + (((COALESCE(m.memtotal, 0) + + COALESCE(l.leadertotal, 0)) - actf.pool_fee_fixed) * actf.pool_fee_variable))))) / (NULLIF(actf.active_stake, 0))), 1000) + 1), 73) - 1)) * 100)::numeric, 9) + END + END + END AS epoch_ros + FROM pool_hash AS ph + INNER JOIN activeandfees AS actf ON actf.pool_id = ph."view" + LEFT JOIN blockcounts AS b ON ph.id = b.pool_hash_id + AND actf.epoch_no = b.epoch_no + LEFT JOIN leadertotals AS l ON ph.id = l.pool_id + AND actf.epoch_no = l.earned_epoch + LEFT JOIN membertotals AS m ON ph.id = m.pool_id + AND actf.epoch_no = m.earned_epoch + LEFT JOIN delegators AS del ON ph.id = del.pool_id + AND actf.epoch_no = del.epoch_no + ); + + INSERT INTO grest.control_table (key, last_value) + VALUES ('pool_history_cache_last_updated', NOW() AT TIME ZONE 'utc') + ON CONFLICT (key) + DO UPDATE SET last_value = NOW() AT TIME ZONE 'utc'; + +END; $$; -COMMENT ON FUNCTION grest.pool_history_cache_update IS 'Internal function to update pool history for data from specified epoch until -current-epoch-minus-one. Invoke with non-empty param for initial population, with empty for subsequent updates'; +COMMENT ON FUNCTION grest.pool_history_cache_update IS 'Internal function to update pool history for data FROM specified epoch until current-epoch-minus-one. Invoke WITH non-empty param for initial population, WITH empty for subsequent updates'; --noqa: LT01 diff --git a/files/grest/rpc/01_cached_tables/pool_info_cache.sql b/files/grest/rpc/01_cached_tables/pool_info_cache.sql index 7c7fb138..39beb5f8 100644 --- a/files/grest/rpc/01_cached_tables/pool_info_cache.sql +++ b/files/grest/rpc/01_cached_tables/pool_info_cache.sql @@ -1,127 +1,126 @@ DROP TABLE IF EXISTS grest.pool_info_cache; CREATE TABLE grest.pool_info_cache ( - id SERIAL PRIMARY KEY, - tx_id bigint NOT NULL, - update_id bigint NOT NULL, - tx_hash text, - block_time numeric, - pool_hash_id bigint NOT NULL, - pool_id_bech32 character varying NOT NULL, - pool_id_hex text NOT NULL, - active_epoch_no bigint NOT NULL, - vrf_key_hash text NOT NULL, - margin double precision NOT NULL, - fixed_cost lovelace NOT NULL, - pledge lovelace NOT NULL, - reward_addr character varying, - owners character varying [], - relays jsonb [], - meta_id bigint, - meta_url character varying, - meta_hash text, - pool_status text, - retiring_epoch word31type + id serial PRIMARY KEY, + tx_id bigint NOT NULL, + update_id bigint NOT NULL, + tx_hash text, + block_time numeric, + pool_hash_id bigint NOT NULL, + pool_id_bech32 character varying NOT NULL, + pool_id_hex text NOT NULL, + active_epoch_no bigint NOT NULL, + vrf_key_hash text NOT NULL, + margin double precision NOT NULL, + fixed_cost lovelace NOT NULL, + pledge lovelace NOT NULL, + reward_addr character varying, + owners character varying [], + relays jsonb [], + meta_id bigint, + meta_url character varying, + meta_hash text, + pool_status text, + retiring_epoch word31type ); COMMENT ON TABLE grest.pool_info_cache IS 'A summary of all pool parameters and updates'; -CREATE FUNCTION grest.pool_info_insert ( - _update_id bigint, - _tx_id bigint, - _hash_id bigint, - _active_epoch_no bigint, - _vrf_key_hash hash32type, - _margin double precision, - _fixed_cost lovelace, - _pledge lovelace, - _reward_addr_id bigint, - _meta_id bigint - ) - RETURNS void - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.pool_info_insert( + _update_id bigint, + _tx_id bigint, + _hash_id bigint, + _active_epoch_no bigint, + _vrf_key_hash hash32type, + _margin double precision, + _fixed_cost lovelace, + _pledge lovelace, + _reward_addr_id bigint, + _meta_id bigint +) +RETURNS void +LANGUAGE plpgsql +AS $$ DECLARE - _current_epoch_no word31type; - _retiring_epoch word31type; - _pool_status text; + _current_epoch_no word31type; + _retiring_epoch word31type; + _pool_status text; BEGIN - SELECT COALESCE(MAX(no), 0) INTO _current_epoch_no FROM public.epoch; + SELECT COALESCE(MAX(no), 0) INTO _current_epoch_no FROM public.epoch; - SELECT pr.retiring_epoch INTO _retiring_epoch - FROM public.pool_retire AS pr - WHERE pr.hash_id = _hash_id + SELECT pr.retiring_epoch INTO _retiring_epoch + FROM public.pool_retire AS pr + WHERE pr.hash_id = _hash_id AND pr.announced_tx_id > _tx_id - ORDER BY pr.id - LIMIT 1; - - IF _retiring_epoch IS NULL THEN - _pool_status := 'registered'; - ELSIF _retiring_epoch > _current_epoch_no THEN - _pool_status := 'retiring'; - ELSE - _pool_status := 'retired'; - END IF; - - INSERT INTO grest.pool_info_cache ( - tx_id, - update_id, - tx_hash, - block_time, - pool_hash_id, - pool_id_bech32, - pool_id_hex, - active_epoch_no, - vrf_key_hash, - margin, - fixed_cost, - pledge, - reward_addr, - owners, - relays, - meta_id, - meta_url, - meta_hash, - pool_status, - retiring_epoch - ) + ORDER BY pr.id + LIMIT 1; + + IF _retiring_epoch IS NULL THEN + _pool_status := 'registered'; + ELSIF _retiring_epoch > _current_epoch_no THEN + _pool_status := 'retiring'; + ELSE + _pool_status := 'retired'; + END IF; + + INSERT INTO grest.pool_info_cache ( + tx_id, + update_id, + tx_hash, + block_time, + pool_hash_id, + pool_id_bech32, + pool_id_hex, + active_epoch_no, + vrf_key_hash, + margin, + fixed_cost, + pledge, + reward_addr, + owners, + relays, + meta_id, + meta_url, + meta_hash, + pool_status, + retiring_epoch + ) SELECT - _tx_id, - _update_id, - encode(tx.hash::bytea, 'hex'), - EXTRACT(epoch from b.time), - _hash_id, - ph.view, - encode(ph.hash_raw::bytea, 'hex'), - _active_epoch_no, - encode(_vrf_key_hash::bytea, 'hex'), - _margin, - _fixed_cost, - _pledge, - sa.view, - ARRAY( - SELECT - sa.view - FROM public.pool_owner AS po - INNER JOIN public.stake_address AS sa ON sa.id = po.addr_id - WHERE po.pool_update_id = _update_id - ), - ARRAY( - SELECT JSONB_BUILD_OBJECT( - 'ipv4', pr.ipv4, - 'ipv6', pr.ipv6, - 'dns', pr.dns_name, - 'srv', pr.dns_srv_name, - 'port', pr.port - ) relay - FROM public.pool_relay AS pr - WHERE pr.update_id = _update_id - ), - _meta_id, - pmr.url, - encode(pmr.hash::bytea, 'hex'), - _pool_status, - _retiring_epoch + _tx_id, + _update_id, + encode(tx.hash::bytea, 'hex'), + EXTRACT(EPOCH FROM b.time), + _hash_id, + ph.view, + encode(ph.hash_raw::bytea, 'hex'), + _active_epoch_no, + encode(_vrf_key_hash::bytea, 'hex'), + _margin, + _fixed_cost, + _pledge, + sa.view, + ARRAY( + SELECT sa.view + FROM public.pool_owner AS po + INNER JOIN public.stake_address AS sa ON sa.id = po.addr_id + WHERE po.pool_update_id = _update_id + ), + ARRAY( + SELECT JSONB_BUILD_OBJECT( + 'ipv4', pr.ipv4, + 'ipv6', pr.ipv6, + 'dns', pr.dns_name, + 'srv', pr.dns_srv_name, + 'port', pr.port + ) relay + FROM public.pool_relay AS pr + WHERE pr.update_id = _update_id + ), + _meta_id, + pmr.url, + encode(pmr.hash::bytea, 'hex'), + _pool_status, + _retiring_epoch FROM public.pool_hash AS ph INNER JOIN public.tx ON tx.id = _tx_id INNER JOIN public.block AS b ON b.id = tx.block_id @@ -131,210 +130,198 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.pool_info_insert IS 'Internal function to insert a single pool update'; +COMMENT ON FUNCTION grest.pool_info_insert IS 'Internal function to insert a single pool update'; -- noqa: LT01 -CREATE FUNCTION grest.pool_info_retire_status () - RETURNS TRIGGER - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.pool_info_retire_status() +RETURNS trigger +LANGUAGE plpgsql +AS $$ BEGIN - UPDATE grest.pool_info_cache - SET - pool_status = 'retired' - WHERE - pool_status = 'retiring' - AND - retiring_epoch <= NEW.no; - RETURN NULL; + UPDATE grest.pool_info_cache + SET pool_status = 'retired' + WHERE pool_status = 'retiring' + AND retiring_epoch <= new.no; + RETURN NULL; END; $$; -COMMENT ON FUNCTION grest.pool_info_retire_status IS 'Internal function to update pool_info_cache with new retire status based on epoch switch'; +COMMENT ON FUNCTION grest.pool_info_retire_status IS 'Internal function to update pool_info_cache with new retire status based ON epoch switch'; -- noqa: LT01 -CREATE FUNCTION grest.pool_info_retire_update () - RETURNS TRIGGER - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.pool_info_retire_update() +RETURNS trigger +LANGUAGE plpgsql +AS $$ DECLARE - _current_epoch_no word31type; - _pool_hash_id bigint; - _latest_pool_update_tx_id bigint; - _retiring_epoch word31type; - _pool_status text; + _current_epoch_no word31type; + _pool_hash_id bigint; + _latest_pool_update_tx_id bigint; + _retiring_epoch word31type; + _pool_status text; BEGIN - SELECT COALESCE(MAX(no), 0) INTO _current_epoch_no FROM public.epoch; + SELECT COALESCE(MAX(no), 0) INTO _current_epoch_no FROM public.epoch; + + IF (tg_op = 'DELETE') THEN + _pool_hash_id := OLD.hash_id; + ELSIF (tg_op = 'INSERT') THEN + _pool_hash_id := new.hash_id; + END IF; + SELECT COALESCE(MAX(tx_id), 0) INTO _latest_pool_update_tx_id FROM grest.pool_info_cache AS pic WHERE pic.pool_hash_id = _pool_hash_id; + + SELECT pr.retiring_epoch INTO _retiring_epoch + FROM public.pool_retire AS pr + WHERE pr.hash_id = _pool_hash_id + AND pr.announced_tx_id > _latest_pool_update_tx_id + ORDER BY pr.id + LIMIT 1; + + IF _retiring_epoch IS NULL THEN + _pool_status := 'registered'; + ELSIF _retiring_epoch > _current_epoch_no THEN + _pool_status := 'retiring'; + ELSE + _pool_status := 'retired'; + END IF; + + UPDATE grest.pool_info_cache + SET + pool_status = _pool_status, + retiring_epoch = _retiring_epoch + WHERE pool_hash_id = _pool_hash_id + AND tx_id = _latest_pool_update_tx_id; + + RETURN NULL; +END; +$$; + +COMMENT ON FUNCTION grest.pool_info_retire_update IS 'Internal function to update pool_info cache table based ON insert/delete ON pool_retire table'; -- noqa: LT01 - IF (TG_OP = 'DELETE') THEN - _pool_hash_id := OLD.hash_id; - ELSIF (TG_OP = 'INSERT') THEN - _pool_hash_id := NEW.hash_id; +CREATE OR REPLACE FUNCTION grest.pool_info_update() +RETURNS trigger +LANGUAGE plpgsql +AS $$ +DECLARE + _latest_pool_update_id integer; +BEGIN + IF (tg_table_name = 'pool_update') THEN + IF (tg_op = 'INSERT') THEN + PERFORM grest.pool_info_insert( + new.id, + new.registered_tx_id, + new.hash_id, + new.active_epoch_no, + new.vrf_key_hash, + new.margin, + new.fixed_cost, + new.pledge, + new.reward_addr_id, + new.meta_id + ); + ELSIF (tg_op = 'DELETE') THEN + DELETE FROM grest.pool_info_cache + WHERE tx_id = OLD.registered_tx_id; END IF; - SELECT COALESCE(MAX(tx_id), 0) INTO _latest_pool_update_tx_id FROM grest.pool_info_cache AS pic WHERE pic.pool_hash_id = _pool_hash_id; - - SELECT pr.retiring_epoch INTO _retiring_epoch - FROM public.pool_retire AS pr - WHERE pr.hash_id = _pool_hash_id - AND pr.announced_tx_id > _latest_pool_update_tx_id - ORDER BY pr.id - LIMIT 1; - - IF _retiring_epoch IS NULL THEN - _pool_status := 'registered'; - ELSIF _retiring_epoch > _current_epoch_no THEN - _pool_status := 'retiring'; - ELSE - _pool_status := 'retired'; + + ELSIF (tg_table_name = 'pool_relay') THEN + SELECT pic.id INTO _latest_pool_update_id + FROM grest.pool_info_cache AS pic + INNER JOIN public.pool_update AS pu ON pu.hash_id = pic.pool_hash_id AND pu.registered_tx_id = pic.tx_id + WHERE pu.id = new.update_id; + + IF (_latest_pool_update_id IS NULL) THEN + RETURN NULL; END IF; UPDATE grest.pool_info_cache SET - pool_status = _pool_status, - retiring_epoch = _retiring_epoch + relays = relays || JSONB_BUILD_OBJECT ( + 'ipv4', new.ipv4, + 'ipv6', new.ipv6, + 'dns', new.dns_name, + 'srv', new.dns_srv_name, + 'port', new.port + ) WHERE - pool_hash_id = _pool_hash_id - AND - tx_id = _latest_pool_update_tx_id; - - RETURN NULL; -END; -$$; - -COMMENT ON FUNCTION grest.pool_info_retire_update IS 'Internal function to update pool_info cache table based on insert/delete on pool_retire table'; + id = _latest_pool_update_id; -CREATE FUNCTION grest.pool_info_update () - RETURNS TRIGGER - LANGUAGE plpgsql - AS $$ -DECLARE - _latest_pool_update_id integer; -BEGIN - IF (TG_TABLE_NAME = 'pool_update') THEN - IF (TG_OP = 'INSERT') THEN - PERFORM grest.pool_info_insert( - NEW.id, - NEW.registered_tx_id, - NEW.hash_id, - NEW.active_epoch_no, - NEW.vrf_key_hash, - NEW.margin, - NEW.fixed_cost, - NEW.pledge, - NEW.reward_addr_id, - NEW.meta_id - ); - ELSIF (TG_OP = 'DELETE') THEN - DELETE FROM grest.pool_info_cache - WHERE tx_id = OLD.registered_tx_id; - END IF; - - ELSIF (TG_TABLE_NAME = 'pool_relay') THEN - SELECT pic.id INTO _latest_pool_update_id - FROM grest.pool_info_cache AS pic - INNER JOIN public.pool_update AS pu ON pu.hash_id = pic.pool_hash_id AND pu.registered_tx_id = pic.tx_id - WHERE pu.id = NEW.update_id; - - IF (_latest_pool_update_id IS NULL) THEN - RETURN NULL; - END IF; - - UPDATE grest.pool_info_cache - SET - relays = relays || JSONB_BUILD_OBJECT ( - 'ipv4', NEW.ipv4, - 'ipv6', NEW.ipv6, - 'dns', NEW.dns_name, - 'srv', NEW.dns_srv_name, - 'port', NEW.port - ) - WHERE - id = _latest_pool_update_id; - - ELSIF (TG_TABLE_NAME = 'pool_owner') THEN - UPDATE grest.pool_info_cache - SET - owners = owners || (SELECT sa.view FROM public.stake_address AS sa WHERE sa.id = NEW.addr_id) - WHERE - update_id = NEW.pool_update_id; - END IF; + ELSIF (tg_table_name = 'pool_owner') THEN + UPDATE grest.pool_info_cache + SET + owners = owners || (SELECT sa.view FROM public.stake_address AS sa WHERE sa.id = new.addr_id) + WHERE + update_id = new.pool_update_id; + END IF; - RETURN NULL; + RETURN NULL; END; $$; -COMMENT ON FUNCTION grest.pool_info_update IS 'Internal function to insert/delete pool updates in pool_info cache table'; +COMMENT ON FUNCTION grest.pool_info_update IS 'Internal function to insert/delete pool updates in pool_info cache table'; -- noqa: LT01 - --- Create pool_info_cache trigger based on new/deleted pool updates +-- Create pool_info_cache trigger based ON new/deleted pool updates DROP TRIGGER IF EXISTS pool_info_update_trigger ON public.pool_update; CREATE TRIGGER pool_info_update_trigger - AFTER INSERT OR DELETE ON public.pool_update - FOR EACH ROW - EXECUTE FUNCTION grest.pool_info_update (); +AFTER INSERT OR DELETE ON public.pool_update +FOR EACH ROW EXECUTE FUNCTION grest.pool_info_update(); --- Create pool_info_cache trigger based on new entry in pool_relay +-- Create pool_info_cache trigger based ON new entry in pool_relay DROP TRIGGER IF EXISTS pool_info_update_trigger ON public.pool_relay; CREATE TRIGGER pool_info_update_trigger - AFTER INSERT ON public.pool_relay - FOR EACH ROW - EXECUTE FUNCTION grest.pool_info_update (); +AFTER INSERT ON public.pool_relay +FOR EACH ROW EXECUTE FUNCTION grest.pool_info_update(); --- Create pool_info_cache trigger based on new entry in pool_owner +-- Create pool_info_cache trigger based ON new entry in pool_owner DROP TRIGGER IF EXISTS pool_info_update_trigger ON public.pool_owner; CREATE TRIGGER pool_info_update_trigger - AFTER INSERT ON public.pool_owner - FOR EACH ROW - EXECUTE FUNCTION grest.pool_info_update (); +AFTER INSERT ON public.pool_owner +FOR EACH ROW EXECUTE FUNCTION grest.pool_info_update(); --- Create pool_info_cache trigger based on new/deleted pool retire entries +-- Create pool_info_cache trigger based ON new/deleted pool retire entries DROP TRIGGER IF EXISTS pool_info_retire_update_trigger ON public.pool_retire; CREATE TRIGGER pool_info_retire_update_trigger - AFTER INSERT OR DELETE ON public.pool_retire - FOR EACH ROW - EXECUTE FUNCTION grest.pool_info_retire_update (); +AFTER INSERT OR DELETE ON public.pool_retire +FOR EACH ROW EXECUTE FUNCTION grest.pool_info_retire_update(); --- Create pool_info_cache trigger based on new epoch for retire status update +-- Create pool_info_cache trigger based ON new epoch for retire status update DROP TRIGGER IF EXISTS pool_info_retire_status_trigger ON public.epoch; CREATE TRIGGER pool_info_retire_status_trigger - AFTER INSERT ON public.epoch - FOR EACH ROW - EXECUTE FUNCTION grest.pool_info_retire_status (); +AFTER INSERT ON public.epoch +FOR EACH ROW EXECUTE FUNCTION grest.pool_info_retire_status(); -- Initialize pool_info_cache table with all current pool data DO $$ DECLARE - _latest_pool_info_tx_id bigint; - rec RECORD; + _latest_pool_info_tx_id bigint; + rec RECORD; BEGIN - SELECT COALESCE(MAX(tx_id), 0) INTO _latest_pool_info_tx_id FROM grest.pool_info_cache; - - FOR rec IN ( - SELECT * FROM public.pool_update AS pu WHERE pu.registered_tx_id > _latest_pool_info_tx_id - ) LOOP - PERFORM grest.pool_info_insert( - rec.id, - rec.registered_tx_id, - rec.hash_id, - rec.active_epoch_no, - rec.vrf_key_hash, - rec.margin, - rec.fixed_cost, - rec.pledge, - rec.reward_addr_id, - rec.meta_id - ); - END LOOP; - - CREATE INDEX IF NOT EXISTS idx_tx_id ON grest.pool_info_cache (tx_id); - CREATE INDEX IF NOT EXISTS idx_pool_id_bech32 ON grest.pool_info_cache (pool_id_bech32); - CREATE INDEX IF NOT EXISTS idx_pool_hash_id ON grest.pool_info_cache (pool_hash_id); - CREATE INDEX IF NOT EXISTS idx_pool_status ON grest.pool_info_cache (pool_status); - CREATE INDEX IF NOT EXISTS idx_meta_id ON grest.pool_info_cache (meta_id); + SELECT COALESCE(MAX(tx_id), 0) INTO _latest_pool_info_tx_id FROM grest.pool_info_cache; + + FOR rec IN ( + SELECT * FROM public.pool_update AS pu WHERE pu.registered_tx_id > _latest_pool_info_tx_id + ) LOOP + PERFORM grest.pool_info_insert( + rec.id, + rec.registered_tx_id, + rec.hash_id, + rec.active_epoch_no, + rec.vrf_key_hash, + rec.margin, + rec.fixed_cost, + rec.pledge, + rec.reward_addr_id, + rec.meta_id + ); + END LOOP; + + CREATE INDEX IF NOT EXISTS idx_tx_id ON grest.pool_info_cache (tx_id); + CREATE INDEX IF NOT EXISTS idx_pool_id_bech32 ON grest.pool_info_cache (pool_id_bech32); + CREATE INDEX IF NOT EXISTS idx_pool_hash_id ON grest.pool_info_cache (pool_hash_id); + CREATE INDEX IF NOT EXISTS idx_pool_status ON grest.pool_info_cache (pool_status); + CREATE INDEX IF NOT EXISTS idx_meta_id ON grest.pool_info_cache (meta_id); END; $$; - diff --git a/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql b/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql index 4f33942e..74cc44a9 100644 --- a/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql +++ b/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql @@ -1,14 +1,16 @@ -CREATE TABLE IF NOT EXISTS GREST.STAKE_DISTRIBUTION_CACHE ( - STAKE_ADDRESS varchar PRIMARY KEY, - POOL_ID varchar, - TOTAL_BALANCE numeric, - UTXO numeric, - REWARDS numeric, - WITHDRAWALS numeric, - REWARDS_AVAILABLE numeric +CREATE TABLE IF NOT EXISTS grest.stake_distribution_cache ( + stake_address varchar PRIMARY KEY, + pool_id varchar, + total_balance numeric, + utxo numeric, + rewards numeric, + withdrawals numeric, + rewards_available numeric ); -CREATE OR REPLACE PROCEDURE GREST.UPDATE_STAKE_DISTRIBUTION_CACHE () LANGUAGE PLPGSQL AS $$ +CREATE OR REPLACE PROCEDURE grest.update_stake_distribution_cache() +LANGUAGE plpgsql +AS $$ DECLARE -- Last block height to control future re-runs of the query _last_accounted_block_height bigint; _last_account_tx_id bigint; @@ -16,216 +18,252 @@ DECLARE -- Last block height to control future re-runs of the query _last_active_stake_blockid bigint; _latest_epoch bigint; BEGIN - - SELECT MAX(block_no) FROM PUBLIC.BLOCK + SELECT MAX(block_no) FROM public.block WHERE block_no IS NOT NULL INTO _last_accounted_block_height; - - SELECT (last_value::integer - 2)::integer INTO _active_stake_epoch FROM GREST.CONTROL_TABLE + SELECT (last_value::integer - 2)::integer INTO _active_stake_epoch FROM grest.control_table WHERE key = 'last_active_stake_validated_epoch'; - SELECT MAX(tx.id) INTO _last_account_tx_id - FROM PUBLIC.TX - INNER JOIN BLOCK b ON b.id = tx.block_id + FROM public.tx + INNER JOIN block AS b ON b.id = tx.block_id WHERE b.epoch_no <= _active_stake_epoch AND b.block_no IS NOT NULL AND b.tx_count != 0; - - SELECT MAX(no) INTO _latest_epoch FROM PUBLIC.EPOCH WHERE NO IS NOT NULL; + SELECT MAX(no) INTO _latest_epoch FROM public.epoch WHERE no IS NOT NULL; WITH accounts_with_delegated_pools AS ( - SELECT DISTINCT ON (STAKE_ADDRESS.ID) stake_address.id as stake_address_id, stake_address.view as stake_address, pool_hash_id - FROM STAKE_ADDRESS - INNER JOIN DELEGATION ON DELEGATION.ADDR_ID = STAKE_ADDRESS.ID + SELECT DISTINCT ON (stake_address.id) + stake_address.id AS stake_address_id, + stake_address.view AS stake_address, + pool_hash_id + FROM stake_address + INNER JOIN delegation ON delegation.addr_id = stake_address.id WHERE NOT EXISTS ( - SELECT TRUE FROM DELEGATION D - WHERE D.ADDR_ID = DELEGATION.ADDR_ID AND D.ID > DELEGATION.ID + SELECT TRUE + FROM delegation AS d + WHERE d.addr_id = delegation.addr_id AND d.id > delegation.id ) AND NOT EXISTS ( - SELECT TRUE FROM STAKE_DEREGISTRATION - WHERE STAKE_DEREGISTRATION.ADDR_ID = DELEGATION.ADDR_ID - AND STAKE_DEREGISTRATION.TX_ID > DELEGATION.TX_ID + SELECT TRUE + FROM stake_deregistration + WHERE stake_deregistration.addr_id = delegation.addr_id + AND stake_deregistration.tx_id > delegation.tx_id ) -- Account must be present in epoch_stake table for the last validated epoch AND EXISTS ( - SELECT TRUE FROM EPOCH_STAKE - WHERE EPOCH_STAKE.EPOCH_NO = ( - SELECT last_value::integer FROM GREST.CONTROL_TABLE - WHERE key = 'last_active_stake_validated_epoch' - ) - AND EPOCH_STAKE.ADDR_ID = STAKE_ADDRESS.ID + SELECT TRUE + FROM epoch_stake + WHERE epoch_stake.epoch_no = ( + SELECT last_value::integer + FROM grest.control_table + WHERE key = 'last_active_stake_validated_epoch' + ) + AND epoch_stake.addr_id = stake_address.id ) ), - pool_ids as ( - SELECT awdp.stake_address_id, + + pool_ids AS ( + SELECT + awdp.stake_address_id, pool_hash.view AS pool_id - FROM POOL_HASH - INNER JOIN accounts_with_delegated_pools awdp ON awdp.pool_hash_id = pool_hash.id + FROM pool_hash + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.pool_hash_id = pool_hash.id ), + account_active_stake AS ( - SELECT awdp.stake_address_id, acsc.amount - FROM grest.account_active_stake_cache acsc - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address = acsc.stake_address - WHERE epoch_no = (_active_stake_epoch + 2) + SELECT + awdp.stake_address_id, + acsc.amount + FROM grest.account_active_stake_cache AS acsc + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address = acsc.stake_address + WHERE epoch_no = (_active_stake_epoch + 2) ), + account_delta_tx_ins AS ( - SELECT awdp.stake_address_id, tx_in.tx_out_id AS txoid, tx_in.tx_out_index AS txoidx FROM tx_in - LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id - WHERE tx_in.tx_in_id > _last_account_tx_id + SELECT + awdp.stake_address_id, + tx_in.tx_out_id AS txoid, + tx_in.tx_out_index AS txoidx + FROM tx_in + LEFT JOIN tx_out + ON tx_in.tx_out_id = tx_out.tx_id + AND tx_in.tx_out_index::smallint = tx_out.index::smallint + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE tx_in.tx_in_id > _last_account_tx_id ), + account_delta_input AS ( - SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + SELECT + tx_out.stake_address_id, + COALESCE(SUM(tx_out.value), 0) AS amount FROM account_delta_tx_ins - LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id - GROUP BY tx_out.stake_address_id + LEFT JOIN tx_out + ON account_delta_tx_ins.txoid=tx_out.tx_id + AND account_delta_tx_ins.txoidx = tx_out.index + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = tx_out.stake_address_id + GROUP BY tx_out.stake_address_id ), + account_delta_output AS ( - SELECT awdp.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + SELECT + awdp.stake_address_id, + COALESCE(SUM(tx_out.value), 0) AS amount FROM tx_out - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id - WHERE TX_OUT.TX_ID > _last_account_tx_id + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE tx_out.tx_id > _last_account_tx_id GROUP BY awdp.stake_address_id ), + account_delta_rewards AS ( - SELECT awdp.stake_address_id, COALESCE(SUM(reward.amount), 0) AS REWARDS - FROM REWARD - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = reward.addr_id + SELECT + awdp.stake_address_id, + COALESCE(SUM(reward.amount), 0) AS rewards + FROM reward + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = reward.addr_id WHERE - ( REWARD.SPENDABLE_EPOCH >= (_active_stake_epoch + 2) AND REWARD.SPENDABLE_EPOCH <= _latest_epoch ) - OR ( REWARD.TYPE = 'refund' AND REWARD.SPENDABLE_EPOCH >= (_active_stake_epoch + 1) AND REWARD.SPENDABLE_EPOCH <= _latest_epoch ) + (reward.spendable_epoch >= (_active_stake_epoch + 2) AND reward.spendable_epoch <= _latest_epoch ) + OR (reward.TYPE = 'refund' AND reward.spendable_epoch >= (_active_stake_epoch + 1) AND reward.spendable_epoch <= _latest_epoch ) GROUP BY awdp.stake_address_id ), + account_delta_withdrawals AS ( - SELECT accounts_with_delegated_pools.stake_address_id, COALESCE(SUM(withdrawal.amount), 0) AS withdrawals + SELECT + accounts_with_delegated_pools.stake_address_id, + COALESCE(SUM(withdrawal.amount), 0) AS withdrawals FROM withdrawal - INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = withdrawal.addr_id + INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = withdrawal.addr_id WHERE withdrawal.tx_id > _last_account_tx_id GROUP BY accounts_with_delegated_pools.stake_address_id ), - account_total_rewards as ( - SELECT accounts_with_delegated_pools.stake_address_id, - COALESCE(SUM(REWARD.AMOUNT), 0) AS REWARDS - FROM REWARD - INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = reward.addr_id - WHERE REWARD.SPENDABLE_EPOCH <= _latest_epoch + + account_total_rewards AS ( + SELECT + accounts_with_delegated_pools.stake_address_id, + COALESCE(SUM(reward.amount), 0) AS rewards + FROM reward + INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = reward.addr_id + WHERE reward.spendable_epoch <= _latest_epoch GROUP BY accounts_with_delegated_pools.stake_address_id ), - account_total_withdrawals as ( - SELECT accounts_with_delegated_pools.stake_address_id, - COALESCE(SUM(WITHDRAWAL.AMOUNT), 0) AS WITHDRAWALS - FROM WITHDRAWAL - INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = WITHDRAWAL.addr_id + + account_total_withdrawals AS ( + SELECT + accounts_with_delegated_pools.stake_address_id, + COALESCE(SUM(withdrawal.amount), 0) AS withdrawals + FROM withdrawal + INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = withdrawal.addr_id GROUP BY accounts_with_delegated_pools.stake_address_id ) -- INSERT QUERY START - INSERT INTO GREST.STAKE_DISTRIBUTION_CACHE + INSERT INTO grest.stake_distribution_cache SELECT awdp.stake_address, pi.pool_id, - COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) AS TOTAL_BALANCE, + COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) AS total_balance, CASE - WHEN ( COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0) ) <= 0 THEN + WHEN ( COALESCE(atrew.rewards, 0) - COALESCE(atw.withdrawals, 0) ) <= 0 THEN COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) ELSE - COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) - (COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0)) - END AS UTXO, - COALESCE(atrew.REWARDS, 0) AS REWARDS, - COALESCE(atw.WITHDRAWALS, 0) AS WITHDRAWALS, + COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) - (COALESCE(atrew.rewards, 0) - COALESCE(atw.withdrawals, 0)) + END AS utxo, + COALESCE(atrew.rewards, 0) AS rewards, + COALESCE(atw.withdrawals, 0) AS withdrawals, CASE - WHEN ( COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0) ) <= 0 THEN 0 - ELSE COALESCE(atrew.REWARDS, 0) - COALESCE(atw.WITHDRAWALS, 0) - END AS REWARDS_AVAILABLE - from accounts_with_delegated_pools awdp - INNER JOIN pool_ids pi ON pi.stake_address_id = awdp.stake_address_id - LEFT JOIN account_active_stake aas ON aas.stake_address_id = awdp.stake_address_id - LEFT JOIN account_total_rewards atrew ON atrew.stake_address_id = awdp.stake_address_id - LEFT JOIN account_total_withdrawals atw ON atw.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_input adi ON adi.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_output ado ON ado.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_rewards adr ON adr.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_withdrawals adw ON adw.stake_address_id = awdp.stake_address_id - ON CONFLICT (STAKE_ADDRESS) DO + WHEN ( COALESCE(atrew.rewards, 0) - COALESCE(atw.withdrawals, 0) ) <= 0 THEN 0 + ELSE COALESCE(atrew.rewards, 0) - COALESCE(atw.withdrawals, 0) + END AS rewards_available + FROM accounts_with_delegated_pools AS awdp + INNER JOIN pool_ids AS pi ON pi.stake_address_id = awdp.stake_address_id + LEFT JOIN account_active_stake AS aas ON aas.stake_address_id = awdp.stake_address_id + LEFT JOIN account_total_rewards AS atrew ON atrew.stake_address_id = awdp.stake_address_id + LEFT JOIN account_total_withdrawals AS atw ON atw.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_input AS adi ON adi.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_output AS ado ON ado.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_rewards AS adr ON adr.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_withdrawals AS adw ON adw.stake_address_id = awdp.stake_address_id + ON CONFLICT (stake_address) DO UPDATE - SET POOL_ID = EXCLUDED.POOL_ID, - TOTAL_BALANCE = EXCLUDED.TOTAL_BALANCE, - UTXO = EXCLUDED.UTXO, - REWARDS = EXCLUDED.REWARDS, - WITHDRAWALS = EXCLUDED.WITHDRAWALS, - REWARDS_AVAILABLE = EXCLUDED.REWARDS_AVAILABLE + SET pool_id = excluded.pool_id, + total_balance = excluded.total_balance, + utxo = excluded.utxo, + rewards = excluded.rewards, + withdrawals = excluded.withdrawals, + rewards_available = excluded.rewards_available ; - INSERT INTO GREST.CONTROL_TABLE (key, last_value) + INSERT INTO grest.control_table (key, last_value) VALUES ( 'stake_distribution_lbh', _last_accounted_block_height ) ON CONFLICT (key) DO UPDATE SET last_value = _last_accounted_block_height; - END; $$; --- HELPER FUNCTION: GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK +-- HELPER FUNCTION: grest.stake_distribution_cache_update_check -- Determines whether or not the stake distribution cache should be updated --- based on the time rule (max once in 60 mins), and ensures previous run completed. - -CREATE OR REPLACE FUNCTION GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK () RETURNS VOID LANGUAGE PLPGSQL AS $$ - DECLARE - _last_update_block_height bigint DEFAULT NULL; - _current_block_height bigint DEFAULT NULL; - _last_update_block_diff bigint DEFAULT NULL; - BEGIN IF ( +-- based ON the time rule (max once in 60 mins), and ensures previous run completed. + +CREATE OR REPLACE FUNCTION grest.stake_distribution_cache_update_check() +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + _last_update_block_height bigint DEFAULT NULL; + _current_block_height bigint DEFAULT NULL; + _last_update_block_diff bigint DEFAULT NULL; +BEGIN + IF ( -- If checking query with the same name there will be 2 results SELECT COUNT(pid) > 1 - FROM pg_stat_activity - WHERE state = 'active' - AND query ILIKE '%GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK(%' - AND datname = ( - SELECT current_database() - ) + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%grest.stake_distribution_cache_update_check(%' + AND datname = ( + SELECT current_database() + ) ) THEN RAISE EXCEPTION 'Previous query still running but should have completed! Exiting...'; - ELSIF ( - -- If checking query with a different name there will be 1 result - SELECT COUNT(pid) > 0 - FROM pg_stat_activity - WHERE state = 'active' - AND query ILIKE '%GREST.UPDATE_NEWLY_REGISTERED_ACCOUNTS_STAKE_DISTRIBUTION_CACHE(%' - AND datname = ( - SELECT current_database() - ) - ) THEN - RAISE EXCEPTION 'New accounts query running! Exiting...'; ELSIF ( - SELECT count(last_value) = 0 FROM GREST.CONTROL_TABLE WHERE key = 'last_active_stake_validated_epoch' + -- If checking query with a different name there will be 1 result + SELECT COUNT(pid) > 0 + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%grest.update_newly_registered_accounts_stake_distribution_cache(%' + AND datname = ( + SELECT current_database() + ) + ) THEN + RAISE EXCEPTION 'New accounts query running! Exiting...'; + ELSIF ( + SELECT count(last_value) = 0 + FROM grest.control_table + WHERE key = 'last_active_stake_validated_epoch' ) OR ( - SELECT ((SELECT MAX(NO) FROM EPOCH) - COALESCE((last_value::integer - 2)::integer, 0 )) > 3 - FROM GREST.CONTROL_TABLE - WHERE key = 'last_active_stake_validated_epoch' + SELECT ((SELECT MAX(no) FROM epoch) - COALESCE((last_value::integer - 2)::integer, 0 )) > 3 + FROM grest.control_table + WHERE key = 'last_active_stake_validated_epoch' ) THEN - RAISE EXCEPTION 'Active Stake cache too far, skipping...'; + RAISE EXCEPTION 'Active Stake cache too far, skipping...'; END IF; -- QUERY START -- SELECT COALESCE( ( SELECT last_value::bigint - FROM GREST.control_table + FROM grest.control_table WHERE key = 'stake_distribution_lbh' ), 0 ) INTO _last_update_block_height; SELECT MAX(block_no) - FROM PUBLIC.BLOCK + FROM public.block WHERE BLOCK_NO IS NOT NULL INTO _current_block_height; - SELECT ( - _current_block_height - _last_update_block_height - ) INTO _last_update_block_diff; + SELECT (_current_block_height - _last_update_block_height) INTO _last_update_block_diff; -- Do nothing until there is a 180 blocks difference in height - 60 minutes theoretical time -- 185 in check because last block height considered is 5 blocks behind tip @@ -235,7 +273,7 @@ CREATE OR REPLACE FUNCTION GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK () RETURN OR _last_update_block_diff < 0 -- Special case for db-sync restart rollback to epoch start ) THEN RAISE NOTICE 'Re-running...'; - CALL GREST.UPDATE_STAKE_DISTRIBUTION_CACHE (); + CALL grest.update_stake_distribution_cache (); ELSE RAISE NOTICE 'Minimum block height difference(180) for update not reached, skipping...'; END IF; @@ -244,6 +282,6 @@ CREATE OR REPLACE FUNCTION GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK () RETURN END; $$; -DROP INDEX IF EXISTS GREST.idx_pool_id; -CREATE INDEX idx_pool_id ON GREST.STAKE_DISTRIBUTION_CACHE (POOL_ID); +DROP INDEX IF EXISTS grest.idx_pool_id; +CREATE INDEX idx_pool_id ON grest.stake_distribution_cache (pool_id); -- Populated by first crontab execution diff --git a/files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql b/files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql index 1cf65e2f..90325710 100644 --- a/files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql +++ b/files/grest/rpc/01_cached_tables/stake_distribution_new_accounts.sql @@ -1,91 +1,97 @@ -CREATE OR REPLACE PROCEDURE GREST.UPDATE_NEWLY_REGISTERED_ACCOUNTS_STAKE_DISTRIBUTION_CACHE() LANGUAGE PLPGSQL AS $$ +CREATE OR REPLACE PROCEDURE grest.update_newly_registered_accounts_stake_distribution_cache() +LANGUAGE plpgsql +AS $$ BEGIN IF ( -- If checking query with a different name there will be 1 result SELECT COUNT(pid) > 1 - FROM pg_stat_activity - WHERE state = 'active' - AND query ILIKE '%GREST.UPDATE_NEWLY_REGISTERED_ACCOUNTS_STAKE_DISTRIBUTION_CACHE(%' - AND query NOT ILIKE '%pg_stat_activity%' - AND datname = ( - SELECT current_database() - ) + FROM pg_stat_activity + WHERE state = 'active' + AND query ILIKE '%grest.update_newly_registered_accounts_stake_distribution_cache(%' + AND query NOT ILIKE '%pg_stat_activity%' + AND datname = ( + SELECT current_database() + ) ) THEN RAISE EXCEPTION 'New accounts query already running! Exiting...'; ELSIF ( -- If checking query with a different name there will be 1 result SELECT COUNT(pid) > 0 - FROM pg_stat_activity + FROM pg_stat_activity WHERE state = 'active' - AND query ILIKE '%GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK(%' + AND query ILIKE '%grest.stake_distribution_cache_update_check(%' AND datname = ( SELECT current_database() ) ) THEN RAISE NOTICE 'Stake distribution query running! Killing it and running new accounts update...'; - CALL grest.kill_queries_partial_match('GREST.STAKE_DISTRIBUTION_CACHE_UPDATE_CHECK('); + CALL grest.kill_queries_partial_match('grest.stake_distribution_cache_update_check('); END IF; WITH newly_registered_accounts AS ( - SELECT DISTINCT ON (STAKE_ADDRESS.ID) - stake_address.id as stake_address_id, - stake_address.view as stake_address, + SELECT DISTINCT ON (stake_address.id) + stake_address.id AS stake_address_id, + stake_address.view AS stake_address, pool_hash_id - FROM STAKE_ADDRESS - INNER JOIN DELEGATION ON DELEGATION.ADDR_ID = STAKE_ADDRESS.ID - WHERE - NOT EXISTS ( - SELECT TRUE FROM DELEGATION D - WHERE D.ADDR_ID = DELEGATION.ADDR_ID AND D.ID > DELEGATION.ID - ) - AND NOT EXISTS ( - SELECT TRUE FROM STAKE_DEREGISTRATION - WHERE STAKE_DEREGISTRATION.ADDR_ID = DELEGATION.ADDR_ID - AND STAKE_DEREGISTRATION.TX_ID > DELEGATION.TX_ID - ) - AND NOT EXISTS ( - SELECT TRUE FROM EPOCH_STAKE - WHERE EPOCH_STAKE.EPOCH_NO = ( - SELECT last_value::integer FROM GREST.CONTROL_TABLE - WHERE key = 'last_active_stake_validated_epoch' - ) - AND EPOCH_STAKE.ADDR_ID = STAKE_ADDRESS.ID - ) + FROM stake_address + INNER JOIN delegation ON delegation.addr_id = stake_address.id + WHERE + NOT EXISTS ( + SELECT TRUE + FROM delegation AS d + WHERE d.addr_id = delegation.addr_id AND d.id > delegation.id + ) + AND NOT EXISTS ( + SELECT TRUE + FROM stake_deregistration + WHERE stake_deregistration.addr_id = delegation.addr_id + AND stake_deregistration.tx_id > delegation.tx_id + ) + AND NOT EXISTS ( + SELECT TRUE + FROM epoch_stake + WHERE epoch_stake.epoch_no = ( + SELECT last_value::integer FROM grest.control_table + WHERE key = 'last_active_stake_validated_epoch' + ) + AND epoch_stake.addr_id = stake_address.id + ) ) -- INSERT QUERY START - INSERT INTO GREST.STAKE_DISTRIBUTION_CACHE + INSERT INTO grest.stake_distribution_cache SELECT nra.stake_address, - ai.delegated_pool as pool_id, + ai.delegated_pool AS pool_id, ai.total_balance::lovelace, ai.utxo::lovelace, ai.rewards::lovelace, ai.withdrawals::lovelace, ai.rewards_available::lovelace - FROM newly_registered_accounts nra, - LATERAL grest.account_info(array[nra.stake_address]) ai - ON CONFLICT (STAKE_ADDRESS) DO + FROM newly_registered_accounts AS nra, + LATERAL grest.account_info(array[nra.stake_address]) AS ai + ON CONFLICT (stake_address) DO UPDATE SET - POOL_ID = EXCLUDED.POOL_ID, - TOTAL_BALANCE = EXCLUDED.total_balance, - UTXO = EXCLUDED.utxo, - REWARDS = EXCLUDED.rewards, - WITHDRAWALS = EXCLUDED.withdrawals, - REWARDS_AVAILABLE = EXCLUDED.rewards_available; + pool_id = EXCLUDED.pool_id, + total_balance = EXCLUDED.total_balance, + utxo = EXCLUDED.utxo, + rewards = EXCLUDED.rewards, + withdrawals = EXCLUDED.withdrawals, + rewards_available = EXCLUDED.rewards_available; -- Clean up de-registered accounts DELETE FROM grest.stake_distribution_cache WHERE stake_address IN ( - SELECT DISTINCT ON (SA.id) - SA.view - FROM STAKE_ADDRESS SA - INNER JOIN STAKE_DEREGISTRATION SD ON SA.ID = SD.ADDR_ID + SELECT DISTINCT ON (sa.id) + sa.view + FROM stake_address AS sa + INNER JOIN stake_deregistration AS sd ON sa.id = sd.addr_id WHERE NOT EXISTS ( - SELECT TRUE FROM STAKE_REGISTRATION SR - WHERE SR.ADDR_ID = SD.ADDR_ID - AND SR.TX_ID >= SD.TX_ID + SELECT TRUE + FROM stake_registration AS sr + WHERE sr.addr_id = sd.addr_id + AND sr.tx_id >= sd.tx_id ) ); END; diff --git a/files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql b/files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql index bfdecf95..faf81f0b 100644 --- a/files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql +++ b/files/grest/rpc/01_cached_tables/stake_snapshot_cache.sql @@ -1,5 +1,5 @@ /* Keeps track of stake snapshots taken at the end of epochs n - 1 and n - 2 */ -CREATE TABLE IF NOT EXISTS GREST.stake_snapshot_cache ( +CREATE TABLE IF NOT EXISTS grest.stake_snapshot_cache ( addr_id integer, pool_id integer, amount numeric, @@ -10,8 +10,8 @@ CREATE TABLE IF NOT EXISTS GREST.stake_snapshot_cache ( CREATE INDEX IF NOT EXISTS _idx_pool_id ON grest.stake_snapshot_cache (pool_id); CREATE INDEX IF NOT EXISTS _idx_addr_id ON grest.stake_snapshot_cache (addr_id); -CREATE OR REPLACE PROCEDURE GREST.CAPTURE_LAST_EPOCH_SNAPSHOT () -LANGUAGE PLPGSQL +CREATE OR REPLACE PROCEDURE grest.capture_last_epoch_snapshot() +LANGUAGE plpgsql AS $$ DECLARE _previous_epoch_no bigint; @@ -24,7 +24,7 @@ BEGIN SELECT COUNT(pid) > 1 FROM pg_stat_activity WHERE state = 'active' - AND query ILIKE '%GREST.CAPTURE_LAST_EPOCH_SNAPSHOT(%' + AND query ILIKE '%grest.capture_last_epoch_snapshot(%' AND datname = ( SELECT current_database() ) @@ -32,10 +32,10 @@ BEGIN RAISE EXCEPTION 'Previous query still running but should have completed! Exiting...'; END IF; - SELECT MAX(NO) - 1 INTO _previous_epoch_no FROM PUBLIC.EPOCH; + SELECT MAX(no) - 1 INTO _previous_epoch_no FROM public.epoch; IF NOT EXISTS ( - SELECT i_last_tx_id from grest.epoch_info_cache + SELECT i_last_tx_id FROM grest.epoch_info_cache WHERE epoch_no = _previous_epoch_no AND i_last_tx_id IS NOT NULL ) THEN @@ -52,15 +52,15 @@ BEGIN -- Set-up interval limits for previous epoch SELECT MAX(tx.id) INTO _lower_bound_account_tx_id - FROM PUBLIC.TX - INNER JOIN BLOCK b ON b.id = tx.block_id + FROM public.tx + INNER JOIN BLOCK AS b ON b.id = tx.block_id WHERE b.epoch_no <= _previous_epoch_no - 2 AND b.block_no IS NOT NULL AND b.tx_count != 0; SELECT MAX(tx.id) INTO _upper_bound_account_tx_id - FROM PUBLIC.TX - INNER JOIN BLOCK b ON b.id = tx.block_id + FROM public.tx + INNER JOIN BLOCK AS b ON b.id = tx.block_id WHERE b.epoch_no <= _previous_epoch_no AND b.block_no IS NOT NULL AND b.tx_count != 0; @@ -88,7 +88,7 @@ BEGIN spendable_epoch bigint, amount lovelace ); - + INSERT INTO rewards_subset SELECT addr_id, type, spendable_epoch, amount FROM reward @@ -96,18 +96,18 @@ BEGIN /* Registered and delegated accounts to be captured (have epoch_stake entries for baseline) */ WITH - latest_non_cancelled_pool_retire as ( + latest_non_cancelled_pool_retire AS ( SELECT DISTINCT ON (pr.hash_id) pr.hash_id, pr.retiring_epoch - FROM pool_retire pr + FROM pool_retire AS pr WHERE pr.announced_tx_id <= _upper_bound_account_tx_id AND pr.retiring_epoch <= _previous_epoch_no AND NOT EXISTS ( SELECT TRUE - FROM pool_update pu - WHERE pu.hash_id = pr.hash_id + FROM pool_update AS pu + WHERE pu.hash_id = pr.hash_id AND ( pu.registered_tx_id > pr.announced_tx_id OR ( @@ -118,14 +118,14 @@ BEGIN AND pu.registered_tx_id <= _upper_bound_account_tx_id AND pu.registered_tx_id <= ( SELECT i_last_tx_id - FROM grest.epoch_info_cache eic + FROM grest.epoch_info_cache AS eic WHERE eic.epoch_no = pr.retiring_epoch - 1 ) ) AND NOT EXISTS ( SELECT TRUE - FROM pool_retire sub_pr - WHERE pr.hash_id = sub_pr.hash_id + FROM pool_retire AS sub_pr + WHERE pr.hash_id = sub_pr.hash_id AND ( sub_pr.announced_tx_id > pr.announced_tx_id OR ( @@ -136,7 +136,7 @@ BEGIN AND sub_pr.announced_tx_id <= _upper_bound_account_tx_id AND sub_pr.announced_tx_id <= ( SELECT i_last_tx_id - FROM grest.epoch_info_cache eic + FROM grest.epoch_info_cache AS eic WHERE eic.epoch_no = pr.retiring_epoch - 1 ) ) @@ -147,17 +147,17 @@ BEGIN INSERT INTO minimum_pool_delegation_tx_ids SELECT DISTINCT ON (pu.hash_id) pu.hash_id, - pu.registered_tx_id as min_tx_id, + pu.registered_tx_id AS min_tx_id, pu.cert_index - FROM pool_update pu - LEFT JOIN latest_non_cancelled_pool_retire lncpr ON lncpr.hash_id = pu.hash_id + FROM pool_update AS pu + LEFT JOIN latest_non_cancelled_pool_retire AS lncpr ON lncpr.hash_id = pu.hash_id WHERE pu.registered_tx_id <= _upper_bound_account_tx_id AND CASE WHEN lncpr.retiring_epoch IS NOT NULL THEN pu.registered_tx_id > ( SELECT i_last_tx_id - FROM grest.epoch_info_cache eic + FROM grest.epoch_info_cache AS eic WHERE eic.epoch_no = lncpr.retiring_epoch - 1 ) ELSE TRUE @@ -166,40 +166,37 @@ BEGIN pu.hash_id, pu.registered_tx_id ASC; INSERT INTO latest_accounts_delegation_txs - SELECT distinct on (d.addr_id) + SELECT distinct ON (d.addr_id) d.addr_id, d.tx_id, d.cert_index, d.pool_hash_id - FROM DELEGATION D + FROM delegation AS d WHERE d.tx_id <= _upper_bound_account_tx_id AND NOT EXISTS ( - SELECT TRUE FROM STAKE_DEREGISTRATION - WHERE STAKE_DEREGISTRATION.ADDR_ID = D.ADDR_ID + SELECT TRUE FROM stake_deregistration + WHERE stake_deregistration.addr_id = d.addr_id AND ( - STAKE_DEREGISTRATION.TX_ID > D.TX_ID + stake_deregistration.tx_id > d.tx_id OR ( - STAKE_DEREGISTRATION.TX_ID = D.TX_ID - AND STAKE_DEREGISTRATION.CERT_INDEX > D.CERT_INDEX + stake_deregistration.tx_id = d.tx_id + AND stake_deregistration.cert_index > d.cert_index ) ) - AND STAKE_DEREGISTRATION.TX_ID <= _upper_bound_account_tx_id + AND stake_deregistration.tx_id <= _upper_bound_account_tx_id ) ORDER BY d.addr_id, d.tx_id DESC; - CREATE INDEX _idx_pool_hash_id ON latest_accounts_delegation_txs (pool_hash_id); - - /* Registered and delegated accounts to be captured (have epoch_stake entries for baseline) */ WITH accounts_with_delegated_pools AS ( SELECT DISTINCT ON (ladt.addr_id) - ladt.addr_id as stake_address_id, + ladt.addr_id AS stake_address_id, ladt.pool_hash_id - FROM latest_accounts_delegation_txs ladt - INNER JOIN minimum_pool_delegation_tx_ids mpdtx ON mpdtx.pool_hash_id = ladt.pool_hash_id + FROM latest_accounts_delegation_txs AS ladt + INNER JOIN minimum_pool_delegation_tx_ids AS mpdtx ON mpdtx.pool_hash_id = ladt.pool_hash_id WHERE ( ladt.tx_id > mpdtx.latest_registered_tx_id @@ -210,43 +207,55 @@ BEGIN ) -- Account must be present in epoch_stake table for the previous epoch AND EXISTS ( - SELECT TRUE FROM epoch_stake es + SELECT TRUE FROM epoch_stake AS es WHERE es.epoch_no = _previous_epoch_no AND es.addr_id = ladt.addr_id ) ), account_active_stake AS ( - SELECT awdp.stake_address_id, es.amount - FROM public.epoch_stake es - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = es.addr_id - WHERE epoch_no = _previous_epoch_no + SELECT + awdp.stake_address_id, + es.amount + FROM public.epoch_stake AS es + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = es.addr_id + WHERE epoch_no = _previous_epoch_no ), account_delta_tx_ins AS ( - SELECT awdp.stake_address_id, tx_in.tx_out_id AS txoid, tx_in.tx_out_index AS txoidx FROM tx_in - LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id - WHERE tx_in.tx_in_id > _lower_bound_account_tx_id + SELECT + awdp.stake_address_id, + tx_in.tx_out_id AS txoid, + tx_in.tx_out_index AS txoidx + FROM tx_in + LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE tx_in.tx_in_id > _lower_bound_account_tx_id AND tx_in.tx_in_id <= _upper_bound_account_tx_id ), account_delta_input AS ( - SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + SELECT + tx_out.stake_address_id, + COALESCE(SUM(tx_out.value), 0) AS amount FROM account_delta_tx_ins - LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id - GROUP BY tx_out.stake_address_id + LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = tx_out.stake_address_id + GROUP BY tx_out.stake_address_id ), account_delta_output AS ( - SELECT awdp.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + SELECT + awdp.stake_address_id, + COALESCE(SUM(tx_out.value), 0) AS amount FROM tx_out - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = tx_out.stake_address_id - WHERE TX_OUT.TX_ID > _lower_bound_account_tx_id - AND TX_OUT.TX_ID <= _upper_bound_account_tx_id + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = tx_out.stake_address_id + WHERE tx_out.tx_id > _lower_bound_account_tx_id + AND tx_out.tx_id <= _upper_bound_account_tx_id GROUP BY awdp.stake_address_id ), account_delta_rewards AS ( - SELECT awdp.stake_address_id, COALESCE(SUM(rs.amount), 0) AS REWARDS - FROM rewards_subset rs - INNER JOIN accounts_with_delegated_pools awdp ON awdp.stake_address_id = rs.stake_address_id + SELECT + awdp.stake_address_id, + COALESCE(SUM(rs.amount), 0) AS rewards + FROM rewards_subset AS rs + INNER JOIN accounts_with_delegated_pools AS awdp ON awdp.stake_address_id = rs.stake_address_id WHERE CASE WHEN rs.type = 'refund' THEN rs.spendable_epoch IN (_previous_epoch_no - 1, _previous_epoch_no) @@ -255,7 +264,9 @@ BEGIN GROUP BY awdp.stake_address_id ), account_delta_withdrawals AS ( - SELECT accounts_with_delegated_pools.stake_address_id, COALESCE(SUM(withdrawal.amount), 0) AS withdrawals + SELECT + accounts_with_delegated_pools.stake_address_id, + COALESCE(SUM(withdrawal.amount), 0) AS withdrawals FROM withdrawal INNER JOIN accounts_with_delegated_pools ON accounts_with_delegated_pools.stake_address_id = withdrawal.addr_id WHERE withdrawal.tx_id > _lower_bound_account_tx_id @@ -263,30 +274,30 @@ BEGIN GROUP BY accounts_with_delegated_pools.stake_address_id ) - INSERT INTO GREST.stake_snapshot_cache - SELECT - awdp.stake_address_id as addr_id, - awdp.pool_hash_id, - COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) as AMOUNT, - _previous_epoch_no as epoch_no - from accounts_with_delegated_pools awdp - LEFT JOIN account_active_stake aas ON aas.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_input adi ON adi.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_output ado ON ado.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_rewards adr ON adr.stake_address_id = awdp.stake_address_id - LEFT JOIN account_delta_withdrawals adw ON adw.stake_address_id = awdp.stake_address_id - ON CONFLICT (addr_id, EPOCH_NO) DO - UPDATE - SET - POOL_ID = EXCLUDED.POOL_ID, - AMOUNT = EXCLUDED.AMOUNT; + INSERT INTO grest.stake_snapshot_cache + SELECT + awdp.stake_address_id AS addr_id, + awdp.pool_hash_id, + COALESCE(aas.amount, 0) + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) AS amount, + _previous_epoch_no AS epoch_no + FROM accounts_with_delegated_pools AS awdp + LEFT JOIN account_active_stake AS aas ON aas.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_input AS adi ON adi.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_output AS ado ON ado.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_rewards AS adr ON adr.stake_address_id = awdp.stake_address_id + LEFT JOIN account_delta_withdrawals AS adw ON adw.stake_address_id = awdp.stake_address_id + ON CONFLICT (addr_id, epoch_no) DO + UPDATE + SET + pool_id = excluded.pool_id, + amount = excluded.amount; /* Newly registered accounts to be captured (they don't have epoch_stake entries for baseline) */ SELECT INTO _newly_registered_account_ids ARRAY_AGG(addr_id) FROM ( SELECT DISTINCT ladt.addr_id - FROM latest_accounts_delegation_txs ladt - INNER JOIN minimum_pool_delegation_tx_ids mpdtx ON mpdtx.pool_hash_id = ladt.pool_hash_id + FROM latest_accounts_delegation_txs AS ladt + INNER JOIN minimum_pool_delegation_tx_ids AS mpdtx ON mpdtx.pool_hash_id = ladt.pool_hash_id WHERE ( ladt.tx_id > mpdtx.latest_registered_tx_id @@ -297,36 +308,49 @@ BEGIN ) -- Account must NOT be present in epoch_stake table for the previous epoch AND NOT EXISTS ( - SELECT TRUE FROM epoch_stake es + SELECT TRUE FROM epoch_stake AS es WHERE es.epoch_no = _previous_epoch_no AND es.addr_id = ladt.addr_id ) ) AS tmp; + WITH account_delta_tx_ins AS ( - SELECT tx_out.stake_address_id, tx_in.tx_out_id AS txoid, tx_in.tx_out_index AS txoidx + SELECT + tx_out.stake_address_id, + tx_in.tx_out_id AS txoid, + tx_in.tx_out_index AS txoidx FROM tx_in - LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint + LEFT JOIN tx_out ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index::smallint = tx_out.index::smallint WHERE tx_in.tx_in_id <= _upper_bound_account_tx_id AND tx_out.stake_address_id = ANY(_newly_registered_account_ids) ), + account_delta_input AS ( - SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + SELECT + tx_out.stake_address_id, + COALESCE(SUM(tx_out.value), 0) AS amount FROM account_delta_tx_ins - LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index + LEFT JOIN tx_out ON account_delta_tx_ins.txoid=tx_out.tx_id AND account_delta_tx_ins.txoidx = tx_out.index WHERE tx_out.stake_address_id = ANY(_newly_registered_account_ids) GROUP BY tx_out.stake_address_id ), + account_delta_output AS ( - SELECT tx_out.stake_address_id, COALESCE(SUM(tx_out.value), 0) AS amount + SELECT + tx_out.stake_address_id, + COALESCE(SUM(tx_out.value), 0) AS amount FROM tx_out - WHERE TX_OUT.TX_ID <= _upper_bound_account_tx_id + WHERE tx_out.tx_id <= _upper_bound_account_tx_id AND tx_out.stake_address_id = ANY(_newly_registered_account_ids) GROUP BY tx_out.stake_address_id ), + account_delta_rewards AS ( - SELECT r.addr_id as stake_address_id, COALESCE(SUM(r.amount), 0) AS REWARDS - FROM REWARD r + SELECT + r.addr_id AS stake_address_id, + COALESCE(SUM(r.amount), 0) AS rewards + FROM REWARD AS r WHERE r.addr_id = ANY(_newly_registered_account_ids) AND CASE WHEN r.type = 'refund' @@ -335,34 +359,37 @@ BEGIN END GROUP BY r.addr_id ), + account_delta_withdrawals AS ( - SELECT withdrawal.addr_id as stake_address_id, COALESCE(SUM(withdrawal.amount), 0) AS withdrawals + SELECT + withdrawal.addr_id AS stake_address_id, + COALESCE(SUM(withdrawal.amount), 0) AS withdrawals FROM withdrawal WHERE withdrawal.tx_id <= _upper_bound_account_tx_id AND withdrawal.addr_id = ANY(_newly_registered_account_ids) GROUP BY withdrawal.addr_id ) - INSERT INTO GREST.stake_snapshot_cache - SELECT - ladt.addr_id, - ladt.pool_hash_id, - COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) as amount, - _previous_epoch_no as epoch_no - FROM latest_accounts_delegation_txs ladt - LEFT JOIN account_delta_input adi ON adi.stake_address_id = ladt.addr_id - LEFT JOIN account_delta_output ado ON ado.stake_address_id = ladt.addr_id - LEFT JOIN account_delta_rewards adr ON adr.stake_address_id = ladt.addr_id - LEFT JOIN account_delta_withdrawals adw ON adw.stake_address_id = ladt.addr_id - WHERE - ladt.addr_id = ANY(_newly_registered_account_ids) - ON CONFLICT (addr_id, epoch_no) DO - UPDATE - SET - pool_id = EXCLUDED.pool_id, - amount = EXCLUDED.amount; + INSERT INTO grest.stake_snapshot_cache + SELECT + ladt.addr_id, + ladt.pool_hash_id, + COALESCE(ado.amount, 0) - COALESCE(adi.amount, 0) + COALESCE(adr.rewards, 0) - COALESCE(adw.withdrawals, 0) AS amount, + _previous_epoch_no AS epoch_no + FROM latest_accounts_delegation_txs AS ladt + LEFT JOIN account_delta_input AS adi ON adi.stake_address_id = ladt.addr_id + LEFT JOIN account_delta_output AS ado ON ado.stake_address_id = ladt.addr_id + LEFT JOIN account_delta_rewards AS adr ON adr.stake_address_id = ladt.addr_id + LEFT JOIN account_delta_withdrawals AS adw ON adw.stake_address_id = ladt.addr_id + WHERE + ladt.addr_id = ANY(_newly_registered_account_ids) + ON CONFLICT (addr_id, epoch_no) DO + UPDATE + SET + pool_id = excluded.pool_id, + amount = excluded.amount; - INSERT INTO GREST.CONTROL_TABLE (key, last_value) + INSERT INTO grest.control_table (key, last_value) VALUES ( 'last_stake_snapshot_epoch', _previous_epoch_no @@ -385,8 +412,8 @@ BEGIN ph.view, _previous_epoch_no + 2, SUM(ssc.amount) - FROM grest.stake_snapshot_cache ssc - INNER JOIN pool_hash ph ON ph.id = ssc.pool_id + FROM grest.stake_snapshot_cache AS ssc + INNER JOIN pool_hash AS ph ON ph.id = ssc.pool_id WHERE epoch_no = _previous_epoch_no GROUP BY ssc.pool_id, ph.view diff --git a/files/grest/rpc/02_indexes/13_1_00.sql b/files/grest/rpc/02_indexes/13_1_00.sql index 4c18b47b..545c0985 100644 --- a/files/grest/rpc/02_indexes/13_1_00.sql +++ b/files/grest/rpc/02_indexes/13_1_00.sql @@ -23,4 +23,4 @@ CREATE UNIQUE INDEX IF NOT EXISTS unique_txin ON tx_in USING btree (tx_out_id, t CREATE UNIQUE INDEX IF NOT EXISTS unique_withdrawal ON public.withdrawal USING btree (addr_id, tx_id); /* Help multi asset queries */ -CREATE INDEX IF NOT EXISTS idx_ma_tx_out_ident ON ma_tx_out (ident) ; +CREATE INDEX IF NOT EXISTS idx_ma_tx_out_ident ON ma_tx_out (ident); diff --git a/files/grest/rpc/account/account_addresses.sql b/files/grest/rpc/account/account_addresses.sql index 55a21bcd..51f9a7b6 100644 --- a/files/grest/rpc/account/account_addresses.sql +++ b/files/grest/rpc/account/account_addresses.sql @@ -1,79 +1,80 @@ -CREATE OR REPLACE FUNCTION grest.account_addresses (_stake_addresses text[], _first_only boolean default false, _empty boolean default false) - RETURNS TABLE ( - stake_address varchar, - addresses jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.account_addresses(_stake_addresses text [], _first_only boolean DEFAULT FALSE, _empty boolean DEFAULT FALSE) +RETURNS TABLE ( + stake_address varchar, + addresses jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE sa_id_list integer[]; BEGIN SELECT INTO sa_id_list - ARRAY_AGG(STAKE_ADDRESS.ID) + ARRAY_AGG(stake_address.ID) FROM - STAKE_ADDRESS + stake_address WHERE - STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + stake_address.VIEW = ANY(_stake_addresses); IF _first_only IS NOT TRUE AND _empty IS NOT TRUE THEN RETURN QUERY WITH txo_addr AS ( - SELECT - DISTINCT ON (address) address, - stake_address_id + SELECT DISTINCT ON (address) + address, + stake_address_id FROM ( SELECT - txo.address, - txo.stake_address_id, + txo.address, + txo.stake_address_id, txo.id FROM - tx_out txo + tx_out AS txo LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id AND txo.index::smallint = tx_in.tx_out_index::smallint - WHERE + WHERE txo.stake_address_id = ANY(sa_id_list) - AND TX_IN.TX_OUT_ID IS NULL - ) x + AND tx_in.tx_out_id IS NULL + ) AS x ) + SELECT - sa.view as stake_address, - JSONB_AGG(txo_addr.address) as addresses + sa.view AS stake_address, + JSONB_AGG(txo_addr.address) AS addresses FROM txo_addr - INNER JOIN STAKE_ADDRESS sa ON sa.id = txo_addr.stake_address_id + INNER JOIN stake_address AS sa ON sa.id = txo_addr.stake_address_id GROUP BY sa.id; ELSE RETURN QUERY WITH txo_addr AS ( - SELECT - DISTINCT ON (address) address, - stake_address_id + SELECT DISTINCT ON (address) + address, + stake_address_id FROM ( SELECT - txo.address, - txo.stake_address_id, + txo.address, + txo.stake_address_id, txo.id FROM - tx_out txo - WHERE + tx_out AS txo + WHERE txo.stake_address_id = ANY(sa_id_list) LIMIT (CASE WHEN _first_only IS TRUE THEN 1 ELSE NULL END) - ) x + ) AS x ) + SELECT - sa.view as stake_address, - JSONB_AGG(txo_addr.address) as addresses + sa.view AS stake_address, + JSONB_AGG(txo_addr.address) AS addresses FROM txo_addr - INNER JOIN STAKE_ADDRESS sa ON sa.id = txo_addr.stake_address_id + INNER JOIN stake_address AS sa ON sa.id = txo_addr.stake_address_id GROUP BY sa.id; END IF; END; $$; -COMMENT ON FUNCTION grest.account_addresses IS 'Get all addresses associated with given accounts, optionally filtered by first used address only or inclusion of used but empty(no utxo) addresses.'; - +COMMENT ON FUNCTION grest.account_addresses IS 'Get all addresses associated with given accounts, optionally filtered by first used address only or inclusion of used but empty(no utxo) addresses.'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_assets.sql b/files/grest/rpc/account/account_assets.sql index 9f7b0f8c..06397515 100644 --- a/files/grest/rpc/account/account_assets.sql +++ b/files/grest/rpc/account/account_assets.sql @@ -1,20 +1,19 @@ -CREATE OR REPLACE FUNCTION grest.account_assets (_stake_addresses text[]) - RETURNS TABLE ( - stake_address varchar, - asset_list jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.account_assets(_stake_addresses text []) +RETURNS TABLE ( + stake_address varchar, + asset_list jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE sa_id_list integer[]; BEGIN SELECT INTO sa_id_list - ARRAY_AGG(STAKE_ADDRESS.ID) + ARRAY_AGG(stake_address.id) FROM - STAKE_ADDRESS + stake_address WHERE - STAKE_ADDRESS.VIEW = ANY(_stake_addresses); - + stake_address.view = ANY(_stake_addresses); RETURN QUERY WITH _all_assets AS ( SELECT @@ -22,45 +21,44 @@ BEGIN ma.policy, ma.name, ma.fingerprint, - COALESCE(aic.decimals, 0) as decimals, - SUM(mtx.quantity) as quantity + COALESCE(aic.decimals, 0) AS decimals, + SUM(mtx.quantity) AS quantity FROM - MA_TX_OUT MTX - INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident - INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID - INNER JOIN STAKE_ADDRESS sa ON sa.id = TXO.stake_address_id - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id - LEFT JOIN TX_IN on TXO.TX_ID = TX_IN.TX_OUT_ID - AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint + ma_tx_out AS mtx + INNER JOIN multi_asset AS ma ON ma.id = mtx.ident + INNER JOIN tx_out AS txo ON txo.id = mtx.tx_out_id + INNER JOIN stake_address AS sa ON sa.id = txo.stake_address_id + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id + AND txo.index::smallint = tx_in.tx_out_index::smallint WHERE sa.id = ANY(sa_id_list) - AND TX_IN.TX_OUT_ID IS NULL + AND tx_in.tx_out_id IS NULL GROUP BY - sa.view, MA.policy, MA.name, MA.fingerprint, aic.decimals + sa.view, ma.policy, ma.name, ma.fingerprint, aic.decimals ) - SELECT - assets_grouped.view as stake_address, - assets_grouped.assets - FROM ( SELECT - aa.view, - JSONB_AGG( - JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(aa.policy, 'hex'), - 'asset_name', ENCODE(aa.name, 'hex'), - 'fingerprint', aa.fingerprint, - 'decimals', COALESCE(aa.decimals, 0), - 'quantity', aa.quantity::text - ) - ) as assets - FROM - _all_assets aa - GROUP BY - aa.view - ) assets_grouped; + assets_grouped.view AS stake_address, + assets_grouped.assets + FROM ( + SELECT + aa.view, + JSONB_AGG( + JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(aa.policy, 'hex'), + 'asset_name', ENCODE(aa.name, 'hex'), + 'fingerprint', aa.fingerprint, + 'decimals', COALESCE(aa.decimals, 0), + 'quantity', aa.quantity::text + ) + ) AS assets + FROM + _all_assets AS aa + GROUP BY + aa.view + ) AS assets_grouped; END; $$; -COMMENT ON FUNCTION grest.account_assets IS 'Get the native asset balance of given accounts'; - +COMMENT ON FUNCTION grest.account_assets IS 'Get the native asset balance of given accounts'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_history.sql b/files/grest/rpc/account/account_history.sql index a3ec3b7c..c4a33884 100644 --- a/files/grest/rpc/account/account_history.sql +++ b/files/grest/rpc/account/account_history.sql @@ -1,24 +1,24 @@ -CREATE FUNCTION grest.account_history (_stake_addresses text[], _epoch_no integer DEFAULT NULL) - RETURNS TABLE ( - stake_address varchar, - history jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.account_history(_stake_addresses text [], _epoch_no integer DEFAULT NULL) +RETURNS TABLE ( + stake_address varchar, + history jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE sa_id_list integer[]; BEGIN SELECT INTO sa_id_list - ARRAY_AGG(STAKE_ADDRESS.ID) + ARRAY_AGG(stake_address.id) FROM - STAKE_ADDRESS + stake_address WHERE - STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + stake_address.view = ANY(_stake_addresses); IF _epoch_no IS NOT NULL THEN RETURN QUERY SELECT - sa.view as stake_address, + sa.view AS stake_address, JSONB_AGG( JSONB_BUILD_OBJECT( 'pool_id', ph.view, @@ -27,9 +27,9 @@ BEGIN ) ) FROM - EPOCH_STAKE es - LEFT JOIN stake_address sa ON sa.id = es.addr_id - LEFT JOIN pool_hash ph ON ph.id = es.pool_id + epoch_stake AS es + LEFT JOIN stake_address AS sa ON sa.id = es.addr_id + LEFT JOIN pool_hash AS ph ON ph.id = es.pool_id WHERE es.epoch_no = _epoch_no AND @@ -39,7 +39,7 @@ BEGIN ELSE RETURN QUERY SELECT - sa.view as stake_address, + sa.view AS stake_address, JSONB_AGG( JSONB_BUILD_OBJECT( 'pool_id', ph.view, @@ -48,9 +48,9 @@ BEGIN ) ) FROM - EPOCH_STAKE es - LEFT JOIN stake_address sa ON sa.id = es.addr_id - LEFT JOIN pool_hash ph ON ph.id = es.pool_id + epoch_stake AS es + LEFT JOIN stake_address AS sa ON sa.id = es.addr_id + LEFT JOIN pool_hash AS ph ON ph.id = es.pool_id WHERE sa.id = ANY(sa_id_list) GROUP BY @@ -59,5 +59,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.account_history IS 'Get the active stake history of given accounts'; - +COMMENT ON FUNCTION grest.account_history IS 'Get the active stake history of given accounts'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_info.sql b/files/grest/rpc/account/account_info.sql index cde440cf..e2149b5b 100644 --- a/files/grest/rpc/account/account_info.sql +++ b/files/grest/rpc/account/account_info.sql @@ -1,186 +1,181 @@ -CREATE FUNCTION grest.account_info (_stake_addresses text[]) - RETURNS TABLE ( - stake_address varchar, - STATUS text, - DELEGATED_POOL varchar, - TOTAL_BALANCE text, - UTXO text, - REWARDS text, - WITHDRAWALS text, - REWARDS_AVAILABLE text, - RESERVES text, - TREASURY text) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.account_info(_stake_addresses text []) +RETURNS TABLE ( + stake_address varchar, + status text, + delegated_pool varchar, + total_balance text, + utxo text, + rewards text, + withdrawals text, + rewards_available text, + reserves text, + treasury text +) +LANGUAGE plpgsql +AS $$ DECLARE sa_id_list integer[] DEFAULT NULL; BEGIN SELECT INTO sa_id_list - array_agg(ID) - FROM - STAKE_ADDRESS - WHERE - STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + array_agg(id) + FROM stake_address + WHERE stake_address.view = ANY(_stake_addresses); RETURN QUERY WITH latest_withdrawal_txs AS ( SELECT DISTINCT ON (addr_id) addr_id, tx_id - FROM WITHDRAWAL - WHERE ADDR_ID = ANY(sa_id_list) - ORDER BY addr_id, TX_ID DESC + FROM withdrawal + WHERE addr_id = ANY(sa_id_list) + ORDER BY addr_id, tx_id DESC ), + latest_withdrawal_epochs AS ( SELECT lwt.addr_id, b.epoch_no - FROM BLOCK b - INNER JOIN TX ON TX.BLOCK_ID = b.ID - INNER JOIN latest_withdrawal_txs lwt ON tx.id = lwt.tx_id + FROM block b + INNER JOIN tx ON tx.block_id = b.id + INNER JOIN latest_withdrawal_txs AS lwt ON tx.id = lwt.tx_id ) SELECT - STATUS_T.view as stake_address, - CASE WHEN STATUS_T.REGISTERED = TRUE THEN + status_t.view AS stake_address, + CASE WHEN status_t.registered = TRUE THEN 'registered' ELSE 'not registered' - END AS STATUS, - POOL_T.DELEGATED_POOL, - CASE WHEN (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)) < 0 THEN - (COALESCE(UTXO_T.UTXO, 0) + COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0) + COALESCE(RESERVES_T.RESERVES, 0) + COALESCE(TREASURY_T.TREASURY, 0) - (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)))::text + END AS status, + pool_t.delegated_pool, + CASE WHEN (COALESCE(rewards_t.rewards, 0) - COALESCE(withdrawals_t.withdrawals, 0)) < 0 THEN + (COALESCE(utxo_t.utxo, 0) + COALESCE(rewards_t.rewards, 0) - COALESCE(withdrawals_t.withdrawals, 0) + COALESCE(reserves_t.reserves, 0) + COALESCE(treasury_t.treasury, 0) - (COALESCE(rewards_t.rewards, 0) - COALESCE(withdrawals_t.withdrawals, 0)))::text ELSE - (COALESCE(UTXO_T.UTXO, 0) + COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0) + COALESCE(RESERVES_T.RESERVES, 0) + COALESCE(TREASURY_T.TREASURY, 0))::text - END AS TOTAL_BALANCE, - COALESCE(UTXO_T.UTXO, 0)::text AS UTXO, - COALESCE(REWARDS_T.REWARDS, 0)::text AS REWARDS, - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)::text AS WITHDRAWALS, - CASE WHEN (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0)) <= 0 THEN + (COALESCE(utxo_t.utxo, 0) + COALESCE(rewards_t.rewards, 0) - COALESCE(withdrawals_t.withdrawals, 0) + COALESCE(reserves_t.reserves, 0) + COALESCE(treasury_t.treasury, 0))::text + END AS total_balance, + COALESCE(utxo_t.utxo, 0)::text AS utxo, + COALESCE(rewards_t.rewards, 0)::text AS rewards, + COALESCE(withdrawals_t.withdrawals, 0)::text AS withdrawals, + CASE WHEN (COALESCE(rewards_t.rewards, 0) - COALESCE(withdrawals_t.withdrawals, 0)) <= 0 THEN '0' ELSE - (COALESCE(REWARDS_T.REWARDS, 0) - COALESCE(WITHDRAWALS_T.WITHDRAWALS, 0))::text - END AS REWARDS_AVAILABLE, - COALESCE(RESERVES_T.RESERVES, 0)::text AS RESERVES, - COALESCE(TREASURY_T.TREASURY, 0)::text AS TREASURY - FROM + (COALESCE(rewards_t.rewards, 0) - COALESCE(withdrawals_t.withdrawals, 0))::text + END AS rewards_available, + COALESCE(reserves_t.reserves, 0)::text AS reserves, + COALESCE(treasury_t.treasury, 0)::text AS treasury + FROM ( SELECT sas.id, sas.view, EXISTS ( - SELECT TRUE FROM STAKE_REGISTRATION + SELECT TRUE FROM stake_registration WHERE - STAKE_REGISTRATION.ADDR_ID = sas.id + stake_registration.addr_id = sas.id AND NOT EXISTS ( SELECT TRUE - FROM STAKE_DEREGISTRATION + FROM stake_deregistration WHERE - STAKE_DEREGISTRATION.ADDR_ID = STAKE_REGISTRATION.ADDR_ID - AND STAKE_DEREGISTRATION.TX_ID > STAKE_REGISTRATION.TX_ID + stake_deregistration.addr_id = stake_registration.addr_id + AND stake_deregistration.tx_id > stake_registration.tx_id ) - ) AS REGISTERED + ) AS registered FROM public.stake_address sas WHERE sas.id = ANY(sa_id_list) - ) STATUS_T - LEFT JOIN ( + ) AS status_t + LEFT JOIN ( SELECT delegation.addr_id, - POOL_HASH.VIEW AS DELEGATED_POOL + pool_hash.view AS delegated_pool FROM - DELEGATION - INNER JOIN POOL_HASH ON POOL_HASH.ID = DELEGATION.POOL_HASH_ID + delegation + INNER JOIN pool_hash ON pool_hash.id = delegation.pool_hash_id WHERE - DELEGATION.ADDR_ID = ANY(sa_id_list) + delegation.addr_id = ANY(sa_id_list) AND NOT EXISTS ( - SELECT - TRUE - FROM - DELEGATION D + SELECT TRUE + FROM delegation AS d WHERE - D.ADDR_ID = DELEGATION.ADDR_ID - AND D.ID > DELEGATION.ID) + d.addr_id = delegation.addr_id + AND d.id > delegation.id) AND NOT EXISTS ( - SELECT - TRUE - FROM - STAKE_DEREGISTRATION + SELECT TRUE + FROM stake_deregistration WHERE - STAKE_DEREGISTRATION.ADDR_ID = DELEGATION.ADDR_ID - AND STAKE_DEREGISTRATION.TX_ID > DELEGATION.TX_ID) - ) POOL_T ON POOL_T.addr_id = status_t.id - LEFT JOIN ( + stake_deregistration.addr_id = delegation.addr_id + AND stake_deregistration.tx_id > delegation.tx_id) + ) AS pool_t ON pool_t.addr_id = status_t.id + LEFT JOIN ( SELECT - TX_OUT.STAKE_ADDRESS_ID, - COALESCE(SUM(VALUE), 0) AS UTXO + tx_out.stake_address_id, + COALESCE(SUM(VALUE), 0) AS utxo FROM - TX_OUT - LEFT JOIN TX_IN ON TX_OUT.TX_ID = TX_IN.TX_OUT_ID - AND TX_OUT.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint + tx_out + LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id + AND tx_out.index::smallint = tx_in.tx_out_index::smallint WHERE - TX_OUT.STAKE_ADDRESS_ID = ANY(sa_id_list) - AND TX_IN.TX_OUT_ID IS NULL + tx_out.stake_address_id = ANY(sa_id_list) + AND tx_in.tx_out_id IS NULL GROUP BY tx_out.stake_address_id - ) UTXO_T ON UTXO_T.stake_address_id = status_t.id - LEFT JOIN ( + ) AS utxo_t ON utxo_t.stake_address_id = status_t.id + LEFT JOIN ( SELECT - REWARD.ADDR_ID, - COALESCE(SUM(REWARD.AMOUNT), 0) AS REWARDS + reward.addr_id, + COALESCE(SUM(reward.amount), 0) AS rewards FROM - REWARD + reward WHERE - REWARD.ADDR_ID = ANY(sa_id_list) - AND REWARD.SPENDABLE_EPOCH <= ( - SELECT MAX(NO) - FROM EPOCH + reward.addr_id = ANY(sa_id_list) + AND reward.SPENDABLE_EPOCH <= ( + SELECT MAX(no) + FROM epoch ) GROUP BY - REWARD.ADDR_ID - ) REWARDS_T ON REWARDS_T.addr_id = status_t.id - LEFT JOIN ( + reward.addr_id + ) AS rewards_t ON rewards_t.addr_id = status_t.id + LEFT JOIN ( SELECT - WITHDRAWAL.ADDR_ID, - COALESCE(SUM(WITHDRAWAL.AMOUNT), 0) AS WITHDRAWALS + withdrawal.addr_id, + COALESCE(SUM(withdrawal.amount), 0) AS withdrawals FROM - WITHDRAWAL + withdrawal WHERE - WITHDRAWAL.ADDR_ID = ANY(sa_id_list) + withdrawal.addr_id = ANY(sa_id_list) GROUP BY - WITHDRAWAL.ADDR_ID - ) WITHDRAWALS_T ON WITHDRAWALS_T.addr_id = status_t.id - LEFT JOIN ( + withdrawal.addr_id + ) AS withdrawals_t ON withdrawals_t.addr_id = status_t.id + LEFT JOIN ( SELECT - RESERVE.ADDR_ID, - COALESCE(SUM(RESERVE.AMOUNT), 0) AS RESERVES + reserve.addr_id, + COALESCE(SUM(reserve.amount), 0) AS reserves FROM - RESERVE - INNER JOIN TX ON TX.ID = RESERVE.TX_ID - INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID - INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = reserve.addr_id + reserve + INNER JOIN tx ON tx.id = reserve.tx_id + INNER JOIN block ON block.id = tx.block_id + INNER JOIN latest_withdrawal_epochs AS lwe ON lwe.addr_id = reserve.addr_id WHERE - RESERVE.ADDR_ID = ANY(sa_id_list) - AND BLOCK.EPOCH_NO >= lwe.epoch_no + reserve.addr_id = ANY(sa_id_list) + AND block.epoch_no >= lwe.epoch_no GROUP BY - RESERVE.ADDR_ID - ) RESERVES_T ON RESERVES_T.addr_id = status_t.id - LEFT JOIN ( + reserve.addr_id + ) AS reserves_t ON reserves_t.addr_id = status_t.id + LEFT JOIN ( SELECT - TREASURY.ADDR_ID, - COALESCE(SUM(TREASURY.AMOUNT), 0) AS TREASURY + treasury.addr_id, + COALESCE(SUM(treasury.amount), 0) AS treasury FROM - TREASURY - INNER JOIN TX ON TX.ID = TREASURY.TX_ID - INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID - INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = TREASURY.addr_id + treasury + INNER JOIN tx ON tx.id = treasury.tx_id + INNER JOIN block ON block.id = tx.block_id + INNER JOIN latest_withdrawal_epochs AS lwe ON lwe.addr_id = treasury.addr_id WHERE - TREASURY.ADDR_ID = ANY(sa_id_list) - AND BLOCK.EPOCH_NO >= lwe.epoch_no + treasury.addr_id = ANY(sa_id_list) + AND block.epoch_no >= lwe.epoch_no GROUP BY - TREASURY.ADDR_ID - ) TREASURY_T ON TREASURY_T.addr_id = status_t.id; + treasury.addr_id + ) AS treasury_t ON treasury_t.addr_id = status_t.id; END; $$; -COMMENT ON FUNCTION grest.account_info IS 'Get the account info for given stake addresses'; - +COMMENT ON FUNCTION grest.account_info IS 'Get the account info for given stake addresses'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_info_cached.sql b/files/grest/rpc/account/account_info_cached.sql index 6af91e4b..3a4855ab 100644 --- a/files/grest/rpc/account/account_info_cached.sql +++ b/files/grest/rpc/account/account_info_cached.sql @@ -1,113 +1,114 @@ -CREATE OR REPLACE FUNCTION grest.account_info_cached (_stake_addresses text[]) - RETURNS TABLE ( - stake_address varchar, - STATUS text, - DELEGATED_POOL varchar, - TOTAL_BALANCE text, - UTXO text, - REWARDS text, - WITHDRAWALS text, - REWARDS_AVAILABLE text, - RESERVES text, - TREASURY text) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.account_info_cached(_stake_addresses text []) +RETURNS TABLE ( + stake_address varchar, + status text, + delegated_pool varchar, + total_balance text, + utxo text, + rewards text, + withdrawals text, + rewards_available text, + reserves text, + treasury text +) +LANGUAGE plpgsql +AS $$ DECLARE sa_id_list integer[] DEFAULT NULL; BEGIN SELECT INTO sa_id_list - array_agg(ID) + array_agg(id) FROM - STAKE_ADDRESS + stake_address WHERE - STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + stake_address.view = ANY(_stake_addresses); RETURN QUERY WITH latest_withdrawal_txs AS ( SELECT DISTINCT ON (addr_id) addr_id, tx_id - FROM WITHDRAWAL - WHERE ADDR_ID = ANY(sa_id_list) - ORDER BY addr_id, TX_ID DESC + FROM withdrawal + WHERE addr_id = ANY(sa_id_list) + ORDER BY addr_id, tx_id DESC ), latest_withdrawal_epochs AS ( SELECT lwt.addr_id, b.epoch_no - FROM BLOCK b - INNER JOIN TX ON TX.BLOCK_ID = b.ID - INNER JOIN latest_withdrawal_txs lwt ON tx.id = lwt.tx_id + FROM block AS b + INNER JOIN tx ON tx.block_id = b.id + INNER JOIN latest_withdrawal_txs AS lwt ON tx.id = lwt.tx_id ) SELECT sdc.stake_address, - CASE WHEN STATUS_T.REGISTERED = TRUE THEN + CASE WHEN status_t.registered = TRUE THEN 'registered' ELSE 'not registered' END AS status, - sdc.pool_id as pool_id, + sdc.pool_id AS pool_id, sdc.total_balance::text, sdc.utxo::text, sdc.rewards::text, sdc.withdrawals::text, sdc.rewards_available::text, - COALESCE(RESERVES_T.RESERVES, 0)::text AS RESERVES, - COALESCE(TREASURY_T.TREASURY, 0)::text AS TREASURY + COALESCE(reserves_t.reserves, 0)::text AS reserves, + COALESCE(treasury_t.treasury, 0)::text AS treasury FROM - grest.stake_distribution_cache sdc + grest.stake_distribution_cache AS sdc LEFT JOIN ( SELECT sas.id, sas.view, EXISTS ( - SELECT TRUE FROM STAKE_REGISTRATION + SELECT TRUE FROM stake_registration WHERE - STAKE_REGISTRATION.ADDR_ID = sas.id + stake_registration.addr_id = sas.id AND NOT EXISTS ( SELECT TRUE - FROM STAKE_DEREGISTRATION + FROM stake_deregistration WHERE - STAKE_DEREGISTRATION.ADDR_ID = STAKE_REGISTRATION.ADDR_ID - AND STAKE_DEREGISTRATION.TX_ID > STAKE_REGISTRATION.TX_ID + stake_deregistration.addr_id = stake_registration.addr_id + AND stake_deregistration.tx_id > stake_registration.tx_id ) - ) AS REGISTERED - FROM public.stake_address sas + ) AS registered + FROM public.stake_address AS sas WHERE sas.id = ANY(sa_id_list) - ) STATUS_T ON sdc.stake_address = STATUS_T.view + ) AS status_t ON sdc.stake_address = status_t.view LEFT JOIN ( SELECT - RESERVE.ADDR_ID, - COALESCE(SUM(RESERVE.AMOUNT), 0) AS RESERVES + reserve.addr_id, + COALESCE(SUM(reserve.amount), 0) AS reserves FROM - RESERVE - INNER JOIN TX ON TX.ID = RESERVE.TX_ID - INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID - INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = reserve.addr_id + reserve + INNER JOIN tx ON tx.id = reserve.tx_id + INNER JOIN block ON block.id = tx.block_id + INNER JOIN latest_withdrawal_epochs AS lwe ON lwe.addr_id = reserve.addr_id WHERE - RESERVE.ADDR_ID = ANY(sa_id_list) - AND BLOCK.EPOCH_NO >= lwe.epoch_no + reserve.addr_id = ANY(sa_id_list) + AND block.epoch_no >= lwe.epoch_no GROUP BY - RESERVE.ADDR_ID - ) RESERVES_T ON RESERVES_T.addr_id = status_t.id + reserve.addr_id + ) AS reserves_t ON reserves_t.addr_id = status_t.id LEFT JOIN ( SELECT - TREASURY.ADDR_ID, - COALESCE(SUM(TREASURY.AMOUNT), 0) AS TREASURY + treasury.addr_id, + COALESCE(SUM(treasury.amount), 0) AS treasury FROM - TREASURY - INNER JOIN TX ON TX.ID = TREASURY.TX_ID - INNER JOIN BLOCK ON BLOCK.ID = TX.BLOCK_ID - INNER JOIN latest_withdrawal_epochs lwe ON lwe.addr_id = TREASURY.addr_id + treasury + INNER JOIN tx ON tx.id = treasury.tx_id + INNER JOIN block ON block.id = tx.block_id + INNER JOIN latest_withdrawal_epochs AS lwe ON lwe.addr_id = treasury.addr_id WHERE - TREASURY.ADDR_ID = ANY(sa_id_list) - AND BLOCK.EPOCH_NO >= lwe.epoch_no + treasury.addr_id = ANY(sa_id_list) + AND block.epoch_no >= lwe.epoch_no GROUP BY - TREASURY.ADDR_ID - ) TREASURY_T ON TREASURY_T.addr_id = status_t.id + treasury.addr_id + ) AS treasury_t ON treasury_t.addr_id = status_t.id WHERE sdc.stake_address = ANY(_stake_addresses); END; $$; -COMMENT ON FUNCTION grest.account_info IS 'Get the cached account information for given stake addresses'; +COMMENT ON FUNCTION grest.account_info IS 'Get the cached account information for given stake addresses'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_rewards.sql b/files/grest/rpc/account/account_rewards.sql index 426217df..8593d74d 100644 --- a/files/grest/rpc/account/account_rewards.sql +++ b/files/grest/rpc/account/account_rewards.sql @@ -1,19 +1,19 @@ -CREATE FUNCTION grest.account_rewards (_stake_addresses text[], _epoch_no numeric DEFAULT NULL) - RETURNS TABLE ( - stake_address varchar, - rewards jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.account_rewards(_stake_addresses text [], _epoch_no numeric DEFAULT NULL) +RETURNS TABLE ( + stake_address varchar, + rewards jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE sa_id_list integer[]; BEGIN SELECT INTO sa_id_list - ARRAY_AGG(STAKE_ADDRESS.ID) + ARRAY_AGG(stake_address.id) FROM - STAKE_ADDRESS + stake_address WHERE - STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + stake_address.VIEW = ANY(_stake_addresses); IF _epoch_no IS NULL THEN RETURN QUERY @@ -27,15 +27,14 @@ BEGIN 'type', r.type, 'pool_id', ph.view ) - ) as rewards + ) AS rewards FROM reward AS r LEFT JOIN pool_hash AS ph ON r.pool_id = ph.id - INNER JOIN stake_address sa ON sa.id = r.addr_id + INNER JOIN stake_address AS sa ON sa.id = r.addr_id WHERE r.addr_id = ANY(sa_id_list) - GROUP BY - sa.id; + GROUP BY sa.id; ELSE RETURN QUERY SELECT @@ -48,11 +47,11 @@ BEGIN 'type', r.type, 'pool_id', ph.view ) - ) as rewards + ) AS rewards FROM - reward r - LEFT JOIN pool_hash ph ON r.pool_id = ph.id - INNER JOIN stake_address sa ON sa.id = r.addr_id + reward AS r + LEFT JOIN pool_hash AS ph ON r.pool_id = ph.id + INNER JOIN stake_address AS sa ON sa.id = r.addr_id WHERE r.addr_id = ANY(sa_id_list) AND r.earned_epoch = _epoch_no @@ -62,5 +61,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.account_rewards IS 'Get the full rewards history (including MIR) for given stake addresses, or certain epoch if specified'; - +COMMENT ON FUNCTION grest.account_rewards IS 'Get the full rewards history (including MIR) for given stake addresses, or certain epoch if specified'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_updates.sql b/files/grest/rpc/account/account_updates.sql index 84ad1813..371829d6 100644 --- a/files/grest/rpc/account/account_updates.sql +++ b/files/grest/rpc/account/account_updates.sql @@ -1,31 +1,31 @@ -CREATE FUNCTION grest.account_updates (_stake_addresses text[]) - RETURNS TABLE ( - stake_address varchar, - updates jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.account_updates(_stake_addresses text []) +RETURNS TABLE ( + stake_address varchar, + updates jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE sa_id_list integer[] DEFAULT NULL; BEGIN SELECT INTO sa_id_list - ARRAY_AGG(STAKE_ADDRESS.ID) + ARRAY_AGG(stake_address.id) FROM - STAKE_ADDRESS + stake_address WHERE - STAKE_ADDRESS.VIEW = ANY(_stake_addresses); + stake_address.VIEW = ANY(_stake_addresses); RETURN QUERY SELECT - SA.view as stake_address, + sa.view AS stake_address, JSONB_AGG( JSONB_BUILD_OBJECT( - 'action_type', ACTIONS.action_type, - 'tx_hash', ENCODE(TX.HASH, 'hex'), + 'action_type', actions.action_type, + 'tx_hash', ENCODE(tx.hash, 'hex'), 'epoch_no', b.epoch_no, 'epoch_slot', b.epoch_slot_no, 'absolute_slot', b.slot_no, - 'block_time', EXTRACT(epoch from b.time)::integer + 'block_time', EXTRACT(EPOCH FROM b.time)::integer ) ) FROM ( @@ -35,45 +35,46 @@ BEGIN tx_id, addr_id FROM - STAKE_REGISTRATION + stake_registration WHERE addr_id = ANY(sa_id_list) - ) UNION ( + ) + UNION ( SELECT 'deregistration' AS action_type, tx_id, addr_id FROM - STAKE_DEREGISTRATION + stake_deregistration WHERE addr_id = ANY(sa_id_list) - ) UNION ( + ) + UNION ( SELECT 'delegation' AS action_type, tx_id, addr_id FROM - DELEGATION + delegation WHERE addr_id = ANY(sa_id_list) - ) UNION ( + ) + UNION ( SELECT 'withdrawal' AS action_type, tx_id, addr_id FROM - WITHDRAWAL + withdrawal WHERE addr_id = ANY(sa_id_list) - ) - ) ACTIONS - INNER JOIN TX ON TX.ID = ACTIONS.TX_ID - INNER JOIN STAKE_ADDRESS sa ON sa.id = actions.addr_id - INNER JOIN BLOCK b ON b.id = tx.block_id - GROUP BY - sa.id; + ) + ) AS actions + INNER JOIN tx ON tx.id = actions.tx_id + INNER JOIN stake_address AS sa ON sa.id = actions.addr_id + INNER JOIN block AS b ON b.id = tx.block_id + GROUP BY sa.id; END; $$; -COMMENT ON FUNCTION grest.account_updates IS 'Get updates (registration, deregistration, delegation and withdrawals) for given stake addresses'; - +COMMENT ON FUNCTION grest.account_updates IS 'Get updates (registration, deregistration, delegation and withdrawals) for given stake addresses'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_utxos.sql b/files/grest/rpc/account/account_utxos.sql index 5c0a0027..2692104d 100644 --- a/files/grest/rpc/account/account_utxos.sql +++ b/files/grest/rpc/account/account_utxos.sql @@ -1,36 +1,34 @@ -CREATE OR REPLACE FUNCTION grest.account_utxos (_stake_address text) - RETURNS TABLE ( - tx_hash text, - tx_index smallint, - address varchar, - value text, - block_height word31type, - block_time integer - ) - LANGUAGE PLPGSQL - AS $$ - +CREATE OR REPLACE FUNCTION grest.account_utxos(_stake_address text) +RETURNS TABLE ( + tx_hash text, + tx_index smallint, + address varchar, + value text, + block_height word31type, + block_time integer +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT - ENCODE(tx.hash,'hex') as tx_hash, - tx_out.index::smallint as tx_index, + ENCODE(tx.hash,'hex') AS tx_hash, + tx_out.index::smallint AS tx_index, tx_out.address, - tx_out.value::text as value, - b.block_no as block_height, - EXTRACT(epoch from b.time)::integer as block_time + tx_out.value::text AS value, + b.block_no AS block_height, + EXTRACT(EPOCH FROM b.time)::integer AS block_time FROM tx_out LEFT JOIN tx_in ON tx_in.tx_out_id = tx_out.tx_id AND tx_in.tx_out_index = tx_out.index INNER JOIN tx ON tx.id = tx_out.tx_id - LEFT JOIN block b ON b.id = tx.block_id + LEFT JOIN block AS b ON b.id = tx.block_id WHERE tx_in.tx_out_id IS NULL AND - tx_out.stake_address_id = (select id from stake_address where view = _stake_address); - + tx_out.stake_address_id = (SELECT id FROM stake_address WHERE view = _stake_address); END; $$; -COMMENT ON FUNCTION grest.account_utxos IS 'Get non-empty UTxOs associated with a given stake address'; +COMMENT ON FUNCTION grest.account_utxos IS 'Get non-empty UTxOs associated with a given stake address'; -- noqa: LT01 diff --git a/files/grest/rpc/address/address_assets.sql b/files/grest/rpc/address/address_assets.sql index 29a9c0c8..badacf1d 100644 --- a/files/grest/rpc/address/address_assets.sql +++ b/files/grest/rpc/address/address_assets.sql @@ -1,10 +1,10 @@ -CREATE OR REPLACE FUNCTION grest.address_assets (_addresses text[]) - RETURNS TABLE ( - address varchar, - asset_list jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.address_assets(_addresses text []) +RETURNS TABLE ( + address varchar, + asset_list jsonb +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY @@ -14,20 +14,20 @@ BEGIN ma.policy, ma.name, ma.fingerprint, - COALESCE(aic.decimals, 0) as decimals, - SUM(mtx.quantity) as quantity + COALESCE(aic.decimals, 0) AS decimals, + SUM(mtx.quantity) AS quantity FROM - MA_TX_OUT MTX - INNER JOIN MULTI_ASSET MA ON MA.id = MTX.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id - INNER JOIN TX_OUT TXO ON TXO.ID = MTX.TX_OUT_ID - LEFT JOIN TX_IN ON TXO.TX_ID = TX_IN.TX_OUT_ID - AND TXO.INDEX::smallint = TX_IN.TX_OUT_INDEX::smallint + ma_tx_out AS mtx + INNER JOIN multi_asset AS ma ON ma.id = mtx.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + INNER JOIN tx_out AS txo ON txo.id = mtx.tx_out_id + LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id + AND txo.index::smallint = tx_in.tx_out_index::smallint WHERE - TXO.address = ANY(_addresses) - AND TX_IN.tx_out_id IS NULL + txo.address = ANY(_addresses) + AND tx_in.tx_out_id IS NULL GROUP BY - TXO.address, MA.policy, MA.name, ma.fingerprint, aic.decimals + txo.address, ma.policy, ma.name, ma.fingerprint, aic.decimals ) SELECT @@ -44,14 +44,13 @@ BEGIN 'decimals', aa.decimals, 'quantity', aa.quantity::text ) - ) as asset_list + ) AS asset_list FROM - _all_assets aa + _all_assets AS aa GROUP BY aa.address ) assets_grouped; END; $$; -COMMENT ON FUNCTION grest.address_assets IS 'Get the list of all the assets (policy, name and quantity) for given addresses'; - +COMMENT ON FUNCTION grest.address_assets IS 'Get the list of all the assets (policy, name and quantity) for given addresses'; --noqa: LT01 diff --git a/files/grest/rpc/address/address_info.sql b/files/grest/rpc/address/address_info.sql index 8165951b..e4388cba 100644 --- a/files/grest/rpc/address/address_info.sql +++ b/files/grest/rpc/address/address_info.sql @@ -1,35 +1,33 @@ -CREATE OR REPLACE FUNCTION grest.address_info (_addresses text[]) - RETURNS TABLE ( - address varchar, - balance text, - stake_address character varying, - script_address boolean, - utxo_set jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.address_info(_addresses text []) +RETURNS TABLE ( + address varchar, + balance text, + stake_address character varying, + script_address boolean, + utxo_set jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE known_addresses varchar[]; BEGIN - CREATE TEMPORARY TABLE _known_addresses AS SELECT DISTINCT ON (tx_out.address) tx_out.address, - sa.view as stake_address, - COALESCE(tx_out.address_has_script, 'false') as script_address + sa.view AS stake_address, + COALESCE(tx_out.address_has_script, 'false') AS script_address FROM tx_out - LEFT JOIN stake_address SA on sa.id = tx_out.stake_address_id + LEFT JOIN stake_address sa ON sa.id = tx_out.stake_address_id WHERE - tx_out.address = ANY(_addresses) - ; + tx_out.address = ANY(_addresses); RETURN QUERY WITH _all_utxos AS ( SELECT tx.id, tx.hash, - tx_out.id as txo_id, + tx_out.id AS txo_id, tx_out.address, tx_out.value, tx_out.index, @@ -48,12 +46,13 @@ BEGIN tx_out.address = ANY(_addresses) ) - SELECT - ka.address, - COALESCE(SUM(au.value), '0')::text AS balance, - ka.stake_address, - ka.script_address, - CASE WHEN EXISTS ( + SELECT + ka.address, + COALESCE(SUM(au.value), '0')::text AS balance, + ka.stake_address, + ka.script_address, + CASE + WHEN EXISTS ( SELECT TRUE FROM _all_utxos aus WHERE aus.address = ka.address ) THEN JSONB_AGG( @@ -61,44 +60,50 @@ BEGIN 'tx_hash', ENCODE(au.hash, 'hex'), 'tx_index', au.index, 'block_height', block.block_no, - 'block_time', EXTRACT(epoch from block.time)::integer, + 'block_time', EXTRACT(EPOCH FROM block.time)::integer, 'value', au.value::text, 'datum_hash', ENCODE(au.data_hash, 'hex'), - 'inline_datum', ( CASE WHEN au.inline_datum_id IS NULL THEN NULL - ELSE - JSONB_BUILD_OBJECT( - 'bytes', ENCODE(datum.bytes, 'hex'), - 'value', datum.value - ) + 'inline_datum',( + CASE + WHEN au.inline_datum_id IS NULL THEN + NULL + ELSE + JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) END ), - 'reference_script', ( CASE WHEN au.reference_script_id IS NULL THEN NULL - ELSE - JSONB_BUILD_OBJECT( - 'hash', ENCODE(script.hash, 'hex'), - 'bytes', ENCODE(script.bytes, 'hex'), - 'value', script.json, - 'type', script.type::text, - 'size', script.serialised_size - ) + 'reference_script',( + CASE + WHEN au.reference_script_id IS NULL THEN + NULL + ELSE + JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) END ), 'asset_list', COALESCE( ( SELECT JSONB_AGG(JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', COALESCE(aic.decimals, 0), - 'quantity', MTX.quantity::text - )) + 'quantity', mtx.quantity::text + )) FROM - ma_tx_out MTX - INNER JOIN multi_asset MA ON MA.id = MTX.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + ma_tx_out AS mtx + INNER JOIN multi_asset AS ma ON ma.id = mtx.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id WHERE - MTX.tx_out_id = au.txo_id + mtx.tx_out_id = au.txo_id ), JSONB_BUILD_ARRAY() ) @@ -106,18 +111,17 @@ BEGIN ) ELSE '[]'::jsonb - END as utxo_set + END AS utxo_set FROM - _known_addresses ka - LEFT OUTER JOIN _all_utxos au ON au.address = ka.address + _known_addresses AS ka + LEFT OUTER JOIN _all_utxos AS au ON au.address = ka.address LEFT JOIN public.block ON block.id = au.block_id LEFT JOIN datum ON datum.id = au.inline_datum_id LEFT JOIN script ON script.id = au.reference_script_id GROUP BY - ka.address, ka.stake_address, ka.script_address - ; + ka.address, ka.stake_address, ka.script_address; DROP TABLE _known_addresses; END; $$; -COMMENT ON FUNCTION grest.address_info IS 'Get bulk address info - balance, associated stake address (if any) and UTXO set'; +COMMENT ON FUNCTION grest.address_info IS 'Get bulk address info - balance, associated stake address (if any) and UTXO set'; -- noqa: LT01 diff --git a/files/grest/rpc/address/address_txs.sql b/files/grest/rpc/address/address_txs.sql index 8e047e0e..840be398 100644 --- a/files/grest/rpc/address/address_txs.sql +++ b/files/grest/rpc/address/address_txs.sql @@ -1,19 +1,19 @@ -CREATE OR REPLACE FUNCTION grest.address_txs (_addresses text[], _after_block_height integer DEFAULT 0) - RETURNS TABLE ( - tx_hash text, - epoch_no word31type, - block_height word31type, - block_time integer - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.address_txs(_addresses text [], _after_block_height integer DEFAULT 0) +RETURNS TABLE ( + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer +) +LANGUAGE plpgsql +AS $$ DECLARE _tx_id_min bigint; _tx_id_list bigint[]; BEGIN SELECT INTO _tx_id_min id FROM tx - WHERE block_id >= (SELECT id FROM block WHERE block_no = _after_block_height) + WHERE block_id >= (SELECT id FROM block WHERE block_no >= _after_block_height ORDER BY id limit 1) ORDER BY id limit 1; -- all tx_out & tx_in tx ids @@ -24,7 +24,7 @@ BEGIN FROM tx_out WHERE - address = ANY (_addresses) + address = ANY(_addresses) AND tx_id >= _tx_id_min -- UNION @@ -37,26 +37,25 @@ BEGIN AND tx_out.index = tx_in.tx_out_index WHERE tx_in.tx_in_id IS NOT NULL - AND tx_out.address = ANY (_addresses) + AND tx_out.address = ANY(_addresses) AND tx_in.tx_in_id >= _tx_id_min ) AS tmp; RETURN QUERY SELECT - DISTINCT(ENCODE(tx.hash, 'hex')) as tx_hash, + DISTINCT(ENCODE(tx.hash, 'hex')) AS tx_hash, block.epoch_no, block.block_no, - EXTRACT(epoch from block.time)::integer + EXTRACT(EPOCH FROM block.time)::integer FROM public.tx INNER JOIN public.block ON block.id = tx.block_id WHERE - tx.id = ANY (_tx_id_list) + tx.id = ANY(_tx_id_list) AND block.block_no >= _after_block_height ORDER BY block.block_no DESC; END; $$; -COMMENT ON FUNCTION grest.address_txs IS 'Get the transaction hash list of a Cardano address array, optionally filtering after specified block height (inclusive).'; - +COMMENT ON FUNCTION grest.address_txs IS 'Get the transaction hash list of a Cardano address array, optionally filtering after specified block height (inclusive).'; -- noqa: LT01 diff --git a/files/grest/rpc/address/credential_txs.sql b/files/grest/rpc/address/credential_txs.sql index 926a1b51..d581858a 100644 --- a/files/grest/rpc/address/credential_txs.sql +++ b/files/grest/rpc/address/credential_txs.sql @@ -1,12 +1,12 @@ -CREATE OR REPLACE FUNCTION grest.credential_txs (_payment_credentials text[], _after_block_height integer DEFAULT 0) - RETURNS TABLE ( - tx_hash text, - epoch_no word31type, - block_height word31type, - block_time integer - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.credential_txs(_payment_credentials text [], _after_block_height integer DEFAULT 0) +RETURNS TABLE ( + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer +) +LANGUAGE plpgsql +AS $$ DECLARE _payment_cred_bytea bytea[]; _tx_id_min bigint; @@ -15,8 +15,7 @@ BEGIN -- convert input _payment_credentials array into bytea array SELECT INTO _payment_cred_bytea ARRAY_AGG(cred_bytea) FROM ( - SELECT - DECODE(cred_hex, 'hex') AS cred_bytea + SELECT DECODE(cred_hex, 'hex') AS cred_bytea FROM UNNEST(_payment_credentials) AS cred_hex ) AS tmp; @@ -29,44 +28,41 @@ BEGIN -- all tx_out & tx_in tx ids SELECT INTO _tx_id_list ARRAY_AGG(tx_id) FROM ( - SELECT - tx_id + SELECT tx_id FROM tx_out WHERE - payment_cred = ANY (_payment_cred_bytea) + payment_cred = ANY(_payment_cred_bytea) AND tx_id >= _tx_id_min -- UNION -- - SELECT - tx_in_id AS tx_id + SELECT tx_in_id AS tx_id FROM tx_out LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id AND tx_out.index = tx_in.tx_out_index WHERE tx_in.tx_in_id IS NOT NULL - AND tx_out.payment_cred = ANY (_payment_cred_bytea) + AND tx_out.payment_cred = ANY(_payment_cred_bytea) AND tx_in.tx_in_id >= _tx_id_min ) AS tmp; RETURN QUERY SELECT - DISTINCT(ENCODE(tx.hash, 'hex')) as tx_hash, + DISTINCT(ENCODE(tx.hash, 'hex')) AS tx_hash, block.epoch_no, block.block_no, - EXTRACT(epoch from block.time)::integer + EXTRACT(EPOCH FROM block.time)::integer FROM public.tx INNER JOIN public.block ON block.id = tx.block_id WHERE - tx.id = ANY (_tx_id_list) + tx.id = ANY(_tx_id_list) AND block.block_no >= _after_block_height ORDER BY block.block_no DESC; END; $$; -COMMENT ON FUNCTION grest.address_txs IS 'Get the transaction hash list of a payment credentials array, optionally filtering after specified block height (inclusive).'; - +COMMENT ON FUNCTION grest.address_txs IS 'Get the transaction hash list of a payment credentials array, optionally filtering after specified block height (inclusive).'; --noqa: LT01 diff --git a/files/grest/rpc/address/credential_utxos.sql b/files/grest/rpc/address/credential_utxos.sql index 02b3a1d8..6d9d4677 100644 --- a/files/grest/rpc/address/credential_utxos.sql +++ b/files/grest/rpc/address/credential_utxos.sql @@ -1,11 +1,11 @@ -CREATE OR REPLACE FUNCTION grest.credential_utxos (_payment_credentials text[]) - RETURNS TABLE ( - tx_hash text, - tx_index smallint, - value text - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.credential_utxos(_payment_credentials text []) +RETURNS TABLE ( + tx_hash text, + tx_index smallint, + value text +) +LANGUAGE plpgsql +AS $$ DECLARE _payment_cred_bytea bytea[]; @@ -20,7 +20,7 @@ BEGIN RETURN QUERY SELECT - ENCODE(tx.hash, 'hex')::text as tx_hash, + ENCODE(tx.hash, 'hex')::text AS tx_hash, tx_out.index::smallint, tx_out.value::text AS balance FROM tx_out @@ -28,8 +28,7 @@ BEGIN LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id AND tx_out.index = tx_in.tx_out_index WHERE - payment_cred = any(_payment_cred_bytea) - AND - tx_in.tx_out_id IS NULL; + payment_cred = ANY(_payment_cred_bytea) + AND tx_in.tx_out_id IS NULL; END; $$; diff --git a/files/grest/rpc/assets/asset_addresses.sql b/files/grest/rpc/assets/asset_addresses.sql index f303abc1..3344d395 100644 --- a/files/grest/rpc/assets/asset_addresses.sql +++ b/files/grest/rpc/assets/asset_addresses.sql @@ -1,21 +1,23 @@ -CREATE OR REPLACE FUNCTION grest.asset_address_list (_asset_policy text, _asset_name text default '') - RETURNS TABLE ( - payment_address varchar, - quantity text - ) LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_address_list(_asset_policy text, _asset_name text DEFAULT '') +RETURNS TABLE ( + payment_address varchar, + quantity text +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT * FROM grest.asset_addresses(_asset_policy, _asset_name); END; $$; -CREATE OR REPLACE FUNCTION grest.asset_addresses (_asset_policy text, _asset_name text default '') - RETURNS TABLE ( - payment_address varchar, - quantity text - ) LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_addresses(_asset_policy text, _asset_name text DEFAULT '') +RETURNS TABLE ( + payment_address varchar, + quantity text +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; _asset_name_decoded bytea; @@ -30,8 +32,7 @@ BEGIN END, 'hex' ) INTO _asset_name_decoded; - SELECT id INTO _asset_id FROM multi_asset ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; - + SELECT id INTO _asset_id FROM multi_asset AS ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; RETURN QUERY SELECT x.address, @@ -42,18 +43,18 @@ BEGIN txo.address, mto.quantity FROM - ma_tx_out mto - INNER JOIN tx_out txo ON txo.id = mto.tx_out_id + ma_tx_out AS mto + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id AND txo.index::smallint = tx_in.tx_out_index::smallint WHERE mto.ident = _asset_id AND tx_in.tx_out_id IS NULL - ) x + ) AS x GROUP BY x.address; END; $$; -COMMENT ON FUNCTION grest.asset_address_list IS 'DEPRECATED!! Use asset_addresses instead.'; -COMMENT ON FUNCTION grest.asset_addresses IS 'Returns a list of addresses with quantity holding the specified asset'; +COMMENT ON FUNCTION grest.asset_address_list IS 'DEPRECATED!! Use asset_addresses instead.'; -- noqa: LT01 +COMMENT ON FUNCTION grest.asset_addresses IS 'Returns a list of addresses with quantity holding the specified asset'; -- noqa: LT01 diff --git a/files/grest/rpc/assets/asset_history.sql b/files/grest/rpc/assets/asset_history.sql index 2ba54423..cef2e8b5 100644 --- a/files/grest/rpc/assets/asset_history.sql +++ b/files/grest/rpc/assets/asset_history.sql @@ -1,19 +1,17 @@ -CREATE OR REPLACE FUNCTION grest.asset_history (_asset_policy text, _asset_name text default '') - RETURNS TABLE ( - policy_id text, - asset_name text, - fingerprint character varying, - minting_txs jsonb[] - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_history(_asset_policy text, _asset_name text DEFAULT '') +RETURNS TABLE ( + policy_id text, + asset_name text, + fingerprint character varying, + minting_txs jsonb [] +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; _asset_name_decoded bytea; BEGIN - SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - SELECT DECODE( CASE WHEN _asset_name IS NULL THEN '' @@ -22,7 +20,6 @@ BEGIN END, 'hex' ) INTO _asset_name_decoded; - RETURN QUERY SELECT _asset_policy, @@ -42,36 +39,36 @@ BEGIN ma.fingerprint, tx.id, ENCODE(tx.hash, 'hex') AS tx_hash, - EXTRACT(epoch from b.time)::integer as block_time, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, mtm.quantity::text, - ( CASE WHEN TM.key IS NULL THEN JSONB_BUILD_ARRAY() + ( CASE WHEN tm.key IS NULL THEN JSONB_BUILD_ARRAY() ELSE JSONB_AGG( JSONB_BUILD_OBJECT( - 'key', TM.key::text, - 'json', TM.json + 'key', tm.key::text, + 'json', tm.json ) ) END ) AS metadata FROM - ma_tx_mint mtm - INNER JOIN multi_asset ma ON ma.id = mtm.ident + ma_tx_mint AS mtm + INNER JOIN multi_asset AS ma ON ma.id = mtm.ident INNER JOIN tx ON tx.id = MTM.tx_id - INNER JOIN block b ON b.id = tx.block_id - LEFT JOIN tx_metadata TM ON TM.tx_id = tx.id - WHERE MA.policy = _asset_policy_decoded - AND MA.name = _asset_name_decoded + INNER JOIN block AS b ON b.id = tx.block_id + LEFT JOIN tx_metadata AS tm ON tm.tx_id = tx.id + WHERE ma.policy = _asset_policy_decoded + AND ma.name = _asset_name_decoded GROUP BY ma.fingerprint, tx.id, b.time, mtm.quantity, - TM.key - ) minting_data + tm.key + ) AS minting_data GROUP BY minting_data.fingerprint; END; $$; -COMMENT ON FUNCTION grest.asset_history IS 'Get the mint/burn history of an asset'; +COMMENT ON FUNCTION grest.asset_history IS 'Get the mint/burn history of an asset'; -- noqa: LT01 diff --git a/files/grest/rpc/assets/asset_info.sql b/files/grest/rpc/assets/asset_info.sql index 0a1c6792..48e463b3 100644 --- a/files/grest/rpc/assets/asset_info.sql +++ b/files/grest/rpc/assets/asset_info.sql @@ -1,35 +1,32 @@ -CREATE OR REPLACE FUNCTION grest.asset_info (_asset_policy text, _asset_name text default '') - RETURNS TABLE ( - policy_id text, - asset_name text, - asset_name_ascii text, - fingerprint character varying, - minting_tx_hash text, - total_supply text, - mint_cnt bigint, - burn_cnt bigint, - creation_time integer, - minting_tx_metadata jsonb, - token_registry_metadata jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_info(_asset_policy text, _asset_name text DEFAULT '') +RETURNS TABLE ( + policy_id text, + asset_name text, + asset_name_ascii text, + fingerprint character varying, + minting_tx_hash text, + total_supply text, + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, + minting_tx_metadata jsonb, + token_registry_metadata jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_id_list bigint[]; BEGIN - - -- find all asset id's based on nested array input + -- find all asset id's based ON nested array input SELECT INTO _asset_id_list ARRAY_AGG(id) FROM ( SELECT DISTINCT mu.id FROM - multi_asset mu + multi_asset AS mu WHERE mu.policy = DECODE(_asset_policy, 'hex') AND mu.name = DECODE(_asset_name, 'hex') ) AS tmp; - RETURN QUERY - SELECT ENCODE(ma.policy, 'hex'), ENCODE(ma.name, 'hex'), @@ -39,7 +36,7 @@ BEGIN aic.total_supply::text, aic.mint_cnt, aic.burn_cnt, - EXTRACT(epoch from aic.creation_time)::integer, + EXTRACT(EPOCH FROM aic.creation_time)::integer, metadata.minting_tx_metadata, CASE WHEN arc.name IS NULL THEN NULL ELSE @@ -53,10 +50,10 @@ BEGIN ) END FROM - multi_asset ma - INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id INNER JOIN tx ON tx.id = aic.last_mint_tx_id - LEFT JOIN grest.asset_registry_cache arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name,'hex') + LEFT JOIN grest.asset_registry_cache AS arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name,'hex') LEFT JOIN LATERAL ( SELECT JSONB_OBJECT_AGG( @@ -64,13 +61,12 @@ BEGIN json ) AS minting_tx_metadata FROM - tx_metadata tm + tx_metadata AS tm WHERE tm.tx_id = tx.id ) metadata ON TRUE WHERE - ma.id = any (_asset_id_list); + ma.id = ANY(_asset_id_list); END; $$; - diff --git a/files/grest/rpc/assets/asset_info_bulk.sql b/files/grest/rpc/assets/asset_info_bulk.sql index 0b120867..f03c37c1 100644 --- a/files/grest/rpc/assets/asset_info_bulk.sql +++ b/files/grest/rpc/assets/asset_info_bulk.sql @@ -1,24 +1,23 @@ -CREATE OR REPLACE FUNCTION grest.asset_info (_asset_list text[][]) - RETURNS TABLE ( - policy_id text, - asset_name text, - asset_name_ascii text, - fingerprint character varying, - minting_tx_hash text, - total_supply text, - mint_cnt bigint, - burn_cnt bigint, - creation_time integer, - minting_tx_metadata jsonb, - token_registry_metadata jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_info(_asset_list text [] []) +RETURNS TABLE ( + policy_id text, + asset_name text, + asset_name_ascii text, + fingerprint character varying, + minting_tx_hash text, + total_supply text, + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, + minting_tx_metadata jsonb, + token_registry_metadata jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE - _asset_id_list bigint[]; + _asset_id_list bigint[]; BEGIN - - -- find all asset id's based on nested array input + -- find all asset id's based ON nested array input SELECT INTO _asset_id_list ARRAY_AGG(id) FROM ( SELECT DISTINCT mu.id @@ -27,13 +26,11 @@ BEGIN DECODE(asset_list->>0, 'hex') AS policy, DECODE(asset_list->>1, 'hex') AS name FROM - JSONB_ARRAY_ELEMENTS(TO_JSONB(_asset_list)) asset_list - ) ald - INNER JOIN multi_asset mu ON mu.policy = ald.policy AND mu.name = ald.name + JSONB_ARRAY_ELEMENTS(TO_JSONB(_asset_list)) AS asset_list + ) AS ald + INNER JOIN multi_asset AS mu ON mu.policy = ald.policy AND mu.name = ald.name ) AS tmp; - RETURN QUERY - SELECT ENCODE(ma.policy, 'hex'), ENCODE(ma.name, 'hex'), @@ -43,7 +40,7 @@ BEGIN aic.total_supply::text, aic.mint_cnt, aic.burn_cnt, - EXTRACT(epoch from aic.creation_time)::integer, + EXTRACT(EPOCH FROM aic.creation_time)::integer, metadata.minting_tx_metadata, CASE WHEN arc.name IS NULL THEN NULL ELSE @@ -57,10 +54,10 @@ BEGIN ) END FROM - multi_asset ma - INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id INNER JOIN tx ON tx.id = aic.last_mint_tx_id - LEFT JOIN grest.asset_registry_cache arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name,'hex') + LEFT JOIN grest.asset_registry_cache AS arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name,'hex') LEFT JOIN LATERAL ( SELECT JSONB_OBJECT_AGG( @@ -68,12 +65,12 @@ BEGIN json ) AS minting_tx_metadata FROM - tx_metadata tm + tx_metadata AS tm WHERE tm.tx_id = tx.id ) metadata ON TRUE WHERE - ma.id = any (_asset_id_list); + ma.id = ANY(_asset_id_list); END; $$; diff --git a/files/grest/rpc/assets/asset_nft_address.sql b/files/grest/rpc/assets/asset_nft_address.sql index b32f97b3..daaedab2 100644 --- a/files/grest/rpc/assets/asset_nft_address.sql +++ b/files/grest/rpc/assets/asset_nft_address.sql @@ -1,8 +1,8 @@ -CREATE OR REPLACE FUNCTION grest.asset_nft_address (_asset_policy text, _asset_name text default '') - RETURNS TABLE ( - payment_address varchar - ) LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_nft_address(_asset_policy text, _asset_name text DEFAULT '') +RETURNS TABLE ( + payment_address varchar +) LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; _asset_name_decoded bytea; @@ -20,21 +20,18 @@ BEGIN SELECT id INTO _asset_id FROM - multi_asset ma - INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded AND aic.total_supply = 1; RETURN QUERY - SELECT - address - FROM - tx_out - WHERE - id = (SELECT MAX(tx_out_id) FROM ma_tx_out WHERE ident = _asset_id); + SELECT address + FROM tx_out + WHERE id = (SELECT MAX(tx_out_id) FROM ma_tx_out WHERE ident = _asset_id); END; $$; -COMMENT ON FUNCTION grest.asset_nft_address IS 'Returns the current address holding the specified NFT'; +COMMENT ON FUNCTION grest.asset_nft_address IS 'Returns the current address holding the specified NFT'; -- noqa: LT01 diff --git a/files/grest/rpc/assets/asset_summary.sql b/files/grest/rpc/assets/asset_summary.sql index 914bfadb..04ae7d96 100644 --- a/files/grest/rpc/assets/asset_summary.sql +++ b/files/grest/rpc/assets/asset_summary.sql @@ -1,14 +1,14 @@ -CREATE FUNCTION grest.asset_summary (_asset_policy text, _asset_name text default '') - RETURNS TABLE ( - policy_id text, - asset_name text, - fingerprint character varying, - total_transactions bigint, - staked_wallets bigint, - unstaked_addresses bigint - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_summary(_asset_policy text, _asset_name text DEFAULT '') +RETURNS TABLE ( + policy_id text, + asset_name text, + fingerprint character varying, + total_transactions bigint, + staked_wallets bigint, + unstaked_addresses bigint +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; _asset_name_decoded bytea; @@ -23,59 +23,58 @@ BEGIN END, 'hex' ) INTO _asset_name_decoded; - SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; - -RETURN QUERY - with _asset_utxos as ( - SELECT - TXO.tx_id AS tx_id, - TXO.id AS tx_out_id, - TXO.index AS tx_out_idx, - TXO.address AS address, - TXO.stake_address_id AS sa_id - FROM - ma_tx_out MTO - INNER JOIN tx_out TXO ON TXO.id = MTO.tx_out_id - LEFT JOIN tx_in TXI ON TXI.tx_out_id = TXO.tx_id - WHERE - MTO.ident = _asset_id - AND - TXI.tx_out_id IS NULL) - - SELECT - _asset_policy, - _asset_name, - MA.fingerprint, - ( - SELECT - COUNT(DISTINCT(TXO.tx_id)) - FROM - ma_tx_out MTO - INNER JOIN tx_out TXO ON TXO.id = MTO.tx_out_id - WHERE - ident = _asset_id - ) AS total_transactions, - ( - SELECT - COUNT(DISTINCT(_asset_utxos.sa_id)) - FROM - _asset_utxos - WHERE - _asset_utxos.sa_id IS NOT NULL - ) AS staked_wallets, - ( + SELECT id INTO _asset_id FROM multi_asset AS ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; + RETURN QUERY + with _asset_utxos AS ( SELECT - COUNT(DISTINCT(_asset_utxos.address)) + txo.tx_id AS tx_id, + txo.id AS tx_out_id, + txo.index AS tx_out_idx, + txo.address AS address, + txo.stake_address_id AS sa_id FROM - _asset_utxos + ma_tx_out AS mto + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id + LEFT JOIN tx_in AS txi ON txi.tx_out_id = txo.tx_id WHERE - _asset_utxos.sa_id IS NULL - ) AS unstaked_addresses - FROM - multi_asset MA - WHERE - MA.id = _asset_id; -END; + mto.ident = _asset_id + AND + txi.tx_out_id IS NULL) + + SELECT + _asset_policy, + _asset_name, + ma.fingerprint, + ( + SELECT + COUNT(DISTINCT(txo.tx_id)) + FROM + ma_tx_out mto + INNER JOIN tx_out txo ON txo.id = mto.tx_out_id + WHERE + ident = _asset_id + ) AS total_transactions, + ( + SELECT + COUNT(DISTINCT(_asset_utxos.sa_id)) + FROM + _asset_utxos + WHERE + _asset_utxos.sa_id IS NOT NULL + ) AS staked_wallets, + ( + SELECT + COUNT(DISTINCT(_asset_utxos.address)) + FROM + _asset_utxos + WHERE + _asset_utxos.sa_id IS NULL + ) AS unstaked_addresses + FROM + multi_asset AS ma + WHERE + ma.id = _asset_id; + END; $$; -COMMENT ON FUNCTION grest.asset_summary IS 'Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance)'; +COMMENT ON FUNCTION grest.asset_summary IS 'Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance)'; -- noqa: LT01 diff --git a/files/grest/rpc/assets/asset_txs.sql b/files/grest/rpc/assets/asset_txs.sql index a1c14771..e34fc924 100644 --- a/files/grest/rpc/assets/asset_txs.sql +++ b/files/grest/rpc/assets/asset_txs.sql @@ -1,17 +1,17 @@ -CREATE OR REPLACE FUNCTION grest.asset_txs ( +CREATE OR REPLACE FUNCTION grest.asset_txs( _asset_policy text, - _asset_name text default '', + _asset_name text DEFAULT '', _after_block_height integer DEFAULT 0, - _history boolean DEFAULT false + _history boolean DEFAULT FALSE ) - RETURNS TABLE ( - tx_hash text, - epoch_no word31type, - block_height word31type, - block_time integer - ) - LANGUAGE PLPGSQL - AS $$ +RETURNS TABLE ( + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; _asset_name_decoded bytea; @@ -26,14 +26,14 @@ BEGIN END, 'hex' ) INTO _asset_name_decoded; - SELECT id INTO _asset_id FROM multi_asset MA WHERE MA.policy = _asset_policy_decoded AND MA.name = _asset_name_decoded; + SELECT id INTO _asset_id FROM multi_asset AS ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; RETURN QUERY SELECT - ENCODE(tx_hashes.hash, 'hex') as tx_hash, + ENCODE(tx_hashes.hash, 'hex') AS tx_hash, tx_hashes.epoch_no, tx_hashes.block_no, - EXTRACT(epoch from tx_hashes.time)::integer + EXTRACT(EPOCH FROM tx_hashes.time)::integer FROM ( SELECT DISTINCT ON (tx.hash) tx.hash, @@ -41,24 +41,24 @@ BEGIN block.block_no, block.time FROM - ma_tx_out MTO - INNER JOIN tx_out TXO ON TXO.id = MTO.tx_out_id - INNER JOIN tx ON tx.id = TXO.tx_id + ma_tx_out AS mto + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id + INNER JOIN tx ON tx.id = txo.tx_id INNER JOIN block ON block.id = tx.block_id - LEFT JOIN tx_in TXI ON TXO.tx_id = TXI.tx_out_id - AND TXO.index::smallint = TXI.tx_out_index::smallint + LEFT JOIN tx_in AS txi ON txo.tx_id = txi.tx_out_id + AND txo.index::smallint = txi.tx_out_index::smallint WHERE - MTO.ident = _asset_id + mto.ident = _asset_id AND block.block_no >= _after_block_height - AND (_history = true OR TXI.id IS NULL) + AND (_history = TRUE OR txi.id IS NULL) GROUP BY ident, tx.hash, block.epoch_no, block.block_no, block.time - ) tx_hashes ORDER BY tx_hashes.block_no DESC; + ) AS tx_hashes ORDER BY tx_hashes.block_no DESC; END; $$; -COMMENT ON FUNCTION grest.asset_txs IS 'Get the list of all asset transaction hashes (newest first)'; +COMMENT ON FUNCTION grest.asset_txs IS 'Get the list of all asset transaction hashes (newest first)'; -- noqa: LT01 diff --git a/files/grest/rpc/assets/policy_asset_addresses.sql b/files/grest/rpc/assets/policy_asset_addresses.sql index a0914493..478b1527 100644 --- a/files/grest/rpc/assets/policy_asset_addresses.sql +++ b/files/grest/rpc/assets/policy_asset_addresses.sql @@ -1,15 +1,15 @@ -CREATE OR REPLACE FUNCTION grest.policy_asset_addresses (_asset_policy text) - RETURNS TABLE ( - asset_name text, - payment_address varchar, - quantity text - ) LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.policy_asset_addresses(_asset_policy text) +RETURNS TABLE ( + asset_name text, + payment_address varchar, + quantity text +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - RETURN QUERY WITH _all_assets AS ( @@ -17,7 +17,7 @@ BEGIN id, ENCODE(name, 'hex') AS asset_name FROM - multi_asset ma + multi_asset AS ma WHERE ma.policy = _asset_policy_decoded ) @@ -32,17 +32,17 @@ BEGIN txo.address, mto.quantity FROM - _all_assets aa - INNER JOIN ma_tx_out mto ON mto.ident = aa.id - INNER JOIN tx_out txo ON txo.id = mto.tx_out_id + _all_assets AS aa + INNER JOIN ma_tx_out AS mto ON mto.ident = aa.id + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id AND txo.index::smallint = tx_in.tx_out_index::smallint WHERE tx_in.tx_out_id IS NULL - ) x + ) AS x GROUP BY x.asset_name, x.address; END; $$; -COMMENT ON FUNCTION grest.policy_asset_addresses IS 'Returns a list of addresses with quantity for each asset on a given policy'; +COMMENT ON FUNCTION grest.policy_asset_addresses IS 'Returns a list of addresses with quantity for each asset ON a given policy'; -- noqa: LT01 diff --git a/files/grest/rpc/assets/policy_asset_info.sql b/files/grest/rpc/assets/policy_asset_info.sql index b4d846a6..5c1e910b 100644 --- a/files/grest/rpc/assets/policy_asset_info.sql +++ b/files/grest/rpc/assets/policy_asset_info.sql @@ -1,45 +1,44 @@ -CREATE OR REPLACE FUNCTION grest.asset_policy_info (_asset_policy text) - RETURNS TABLE ( - asset_name text, - asset_name_ascii text, - fingerprint varchar, - minting_tx_hash text, - total_supply text, - mint_cnt bigint, - burn_cnt bigint, - creation_time integer, - minting_tx_metadata jsonb, - token_registry_metadata jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.asset_policy_info(_asset_policy text) +RETURNS TABLE ( + asset_name text, + asset_name_ascii text, + fingerprint varchar, + minting_tx_hash text, + total_supply text, + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, + minting_tx_metadata jsonb, + token_registry_metadata jsonb +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT * FROM grest.policy_asset_info(_asset_policy); END; $$; -CREATE OR REPLACE FUNCTION grest.policy_asset_info (_asset_policy text) - RETURNS TABLE ( - asset_name text, - asset_name_ascii text, - fingerprint varchar, - minting_tx_hash text, - total_supply text, - mint_cnt bigint, - burn_cnt bigint, - creation_time integer, - minting_tx_metadata jsonb, - token_registry_metadata jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.policy_asset_info(_asset_policy text) +RETURNS TABLE ( + asset_name text, + asset_name_ascii text, + fingerprint varchar, + minting_tx_hash text, + total_supply text, + mint_cnt bigint, + burn_cnt bigint, + creation_time integer, + minting_tx_metadata jsonb, + token_registry_metadata jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; _policy_asset_ids bigint[]; BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - RETURN QUERY SELECT ENCODE(ma.name, 'hex') AS asset_name, @@ -49,7 +48,7 @@ BEGIN aic.total_supply::text, aic.mint_cnt, aic.burn_cnt, - EXTRACT(epoch FROM aic.creation_time)::integer, + EXTRACT(EPOCH FROM aic.creation_time)::integer, metadata.minting_tx_metadata, CASE WHEN arc.name IS NULL THEN NULL ELSE @@ -63,10 +62,10 @@ BEGIN ) END FROM - multi_asset ma - INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id INNER JOIN tx ON tx.id = aic.last_mint_tx_id - LEFT JOIN grest.asset_registry_cache arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name, 'hex') + LEFT JOIN grest.asset_registry_cache AS arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name, 'hex') LEFT JOIN LATERAL ( SELECT JSONB_OBJECT_AGG( @@ -74,7 +73,7 @@ BEGIN json ) AS minting_tx_metadata FROM - tx_metadata tm + tx_metadata AS tm WHERE tm.tx_id = tx.id ) metadata ON TRUE @@ -83,5 +82,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.asset_policy_info IS 'Get the asset information of all assets under a policy'; - +COMMENT ON FUNCTION grest.asset_policy_info IS 'Get the asset information of all assets under a policy'; -- noqa: LT01 diff --git a/files/grest/rpc/assets/policy_asset_list.sql b/files/grest/rpc/assets/policy_asset_list.sql index b828be04..630b9aa2 100644 --- a/files/grest/rpc/assets/policy_asset_list.sql +++ b/files/grest/rpc/assets/policy_asset_list.sql @@ -1,32 +1,28 @@ -CREATE OR REPLACE FUNCTION grest.policy_asset_list (_asset_policy text) - RETURNS TABLE ( - asset_name text, - fingerprint varchar, - total_supply text, - decimals integer - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.policy_asset_list(_asset_policy text) +RETURNS TABLE ( + asset_name text, + fingerprint varchar, + total_supply text, + decimals integer +) +LANGUAGE plpgsql +AS $$ DECLARE _asset_policy_decoded bytea; BEGIN - SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - RETURN QUERY - SELECT ENCODE(ma.name, 'hex') AS asset_name, ma.fingerprint AS fingerprint, aic.total_supply::text, aic.decimals FROM - multi_asset ma - INNER JOIN grest.asset_info_cache aic ON aic.asset_id = ma.id + multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id WHERE ma.policy = _asset_policy_decoded; - END; $$; -COMMENT ON FUNCTION grest.asset_policy_info IS 'Get a list of all asset under a policy'; +COMMENT ON FUNCTION grest.asset_policy_info IS 'Get a list of all asset under a policy'; -- noqa: LT01 diff --git a/files/grest/rpc/blocks/block_info.sql b/files/grest/rpc/blocks/block_info.sql index 94de1bac..2aa94a92 100644 --- a/files/grest/rpc/blocks/block_info.sql +++ b/files/grest/rpc/blocks/block_info.sql @@ -1,39 +1,39 @@ -CREATE FUNCTION grest.block_info (_block_hashes text[]) - RETURNS TABLE ( - hash text, - epoch_no word31type, - abs_slot word63type, - epoch_slot word31type, - block_height word31type, - block_size word31type, - block_time integer, - tx_count bigint, - vrf_key varchar, - op_cert text, - op_cert_counter word63type, - pool varchar, - proto_major word31type, - proto_minor word31type, - total_output text, - total_fees text, - num_confirmations integer, - parent_hash text, - child_hash text - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.block_info(_block_hashes text []) +RETURNS TABLE ( + hash text, + epoch_no word31type, + abs_slot word63type, + epoch_slot word31type, + block_height word31type, + block_size word31type, + block_time integer, + tx_count bigint, + vrf_key varchar, + op_cert text, + op_cert_counter word63type, + pool varchar, + proto_major word31type, + proto_minor word31type, + total_output text, + total_fees text, + num_confirmations integer, + parent_hash text, + child_hash text +) +LANGUAGE plpgsql +AS $$ DECLARE _block_hashes_bytea bytea[]; _block_id_list bigint[]; _curr_block_no word31type; BEGIN - SELECT max(block_no) INTO _curr_block_no FROM block b; + SELECT MAX(block_no) INTO _curr_block_no + FROM block AS b; -- convert input _block_hashes array into bytea array SELECT INTO _block_hashes_bytea ARRAY_AGG(hashes_bytea) FROM ( - SELECT - DECODE(hashes_hex, 'hex') AS hashes_bytea + SELECT DECODE(hashes_hex, 'hex') AS hashes_bytea FROM UNNEST(_block_hashes) AS hashes_hex ) AS tmp; @@ -41,52 +41,44 @@ BEGIN -- all block ids SELECT INTO _block_id_list ARRAY_AGG(id) FROM ( - SELECT - id - FROM - block - WHERE block.hash = ANY (_block_hashes_bytea) + SELECT id + FROM block + WHERE block.hash = ANY(_block_hashes_bytea) ) AS tmp; RETURN QUERY SELECT - ENCODE(B.hash, 'hex') AS hash, - B.epoch_no AS epoch, - B.slot_no AS abs_slot, - B.epoch_slot_no AS epoch_slot, - B.block_no AS block_height, - B.size AS block_size, - EXTRACT(epoch from B.time)::integer AS block_time, - B.tx_count, - B.vrf_key, - ENCODE(B.op_cert::bytea, 'hex') as op_cert, - B.op_cert_counter, - PH.view AS pool, - B.proto_major, - B.proto_minor, + ENCODE(b.hash, 'hex') AS hash, + b.epoch_no AS epoch, + b.slot_no AS abs_slot, + b.epoch_slot_no AS epoch_slot, + b.block_no AS block_height, + b.size AS block_size, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + b.tx_count, + b.vrf_key, + ENCODE(b.op_cert::bytea, 'hex') AS op_cert, + b.op_cert_counter, + ph.view AS pool, + b.proto_major, + b.proto_minor, block_data.total_output::text, block_data.total_fees::text, - (_curr_block_no - B.block_no) AS num_confirmations, + (_curr_block_no - b.block_no) AS num_confirmations, ( - SELECT - ENCODE(tB.hash::bytea, 'hex') - FROM - block tB - WHERE - id = b.previous_id + SELECT ENCODE(tb.hash::bytea, 'hex') + FROM block tb + WHERE id = b.previous_id ) AS parent_hash, ( - SELECT - ENCODE(tB.hash::bytea, 'hex') - FROM - block tB - WHERE - previous_id = b.id + SELECT ENCODE(tb.hash::bytea, 'hex') + FROM block tb + WHERE previous_id = b.id ) AS child_hash FROM - block B - LEFT JOIN slot_leader SL ON SL.id = B.slot_leader_id - LEFT JOIN pool_hash PH ON PH.id = SL.pool_hash_id + block AS b + LEFT JOIN slot_leader AS sl ON sl.id = b.slot_leader_id + LEFT JOIN pool_hash AS ph ON ph.id = sl.pool_hash_id LEFT JOIN LATERAL ( SELECT SUM(tx_data.total_output) AS total_output, @@ -94,19 +86,16 @@ BEGIN FROM tx JOIN LATERAL ( - SELECT - SUM(tx_out.value) AS total_output - FROM - tx_out - WHERE - tx_out.tx_id = tx.id + SELECT SUM(tx_out.value) AS total_output + FROM tx_out + WHERE tx_out.tx_id = tx.id ) tx_data ON TRUE WHERE tx.block_id = b.id ) block_data ON TRUE WHERE - B.id = ANY (_block_id_list); + b.id = ANY(_block_id_list); END; $$; -COMMENT ON FUNCTION grest.block_info IS 'Get detailed information about list of block hashes'; +COMMENT ON FUNCTION grest.block_info IS 'Get detailed information about list of block hashes'; --noqa: LT01 diff --git a/files/grest/rpc/blocks/block_txs.sql b/files/grest/rpc/blocks/block_txs.sql index 7710dbb8..8bdeeaf6 100644 --- a/files/grest/rpc/blocks/block_txs.sql +++ b/files/grest/rpc/blocks/block_txs.sql @@ -1,43 +1,34 @@ -CREATE FUNCTION grest.block_txs (_block_hashes text[]) - RETURNS TABLE ( - block_hash text, - tx_hashes text[] - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.block_txs(_block_hashes text []) +RETURNS TABLE ( + block_hash text, + tx_hashes text [] +) +LANGUAGE plpgsql +AS $$ DECLARE _block_hashes_bytea bytea[]; - _BLOCK_IDS integer[]; + _block_ids integer[]; BEGIN - SELECT INTO _block_hashes_bytea - ARRAY_AGG(block_hashes_bytea) + SELECT INTO _block_hashes_bytea ARRAY_AGG(block_hashes_bytea) FROM ( - SELECT - DECODE(hex, 'hex') AS block_hashes_bytea - FROM - UNNEST(_block_hashes) AS hex + SELECT DECODE(hex, 'hex') AS block_hashes_bytea + FROM UNNEST(_block_hashes) AS hex ) AS tmp; - SELECT INTO _BLOCK_IDS - ARRAY_AGG(B.ID) - FROM - public.BLOCK B - WHERE - B.HASH = ANY(_block_hashes_bytea); + SELECT INTO _block_ids ARRAY_AGG(b.id) + FROM public.block AS b + WHERE b.hash = ANY(_block_hashes_bytea); RETURN QUERY SELECT encode(b.hash, 'hex'), - ARRAY_AGG(ENCODE(TX.HASH::bytea, 'hex')) + ARRAY_AGG(ENCODE(tx.hash::bytea, 'hex')) FROM - public.BLOCK B - INNER JOIN public.TX ON TX.BLOCK_ID = B.ID - WHERE - B.ID = ANY(_BLOCK_IDS) - GROUP BY - B.hash; + public.block AS b + INNER JOIN public.tx ON tx.block_id = b.id + WHERE b.id = ANY(_block_ids) + GROUP BY b.hash; END; $$; -COMMENT ON FUNCTION grest.block_txs IS 'Get all transactions contained in given blocks'; - +COMMENT ON FUNCTION grest.block_txs IS 'Get all transactions contained in given blocks'; -- noqa: LT01 diff --git a/files/grest/rpc/epoch/epoch_block_protocols.sql b/files/grest/rpc/epoch/epoch_block_protocols.sql index 1974bcc1..86c73c05 100644 --- a/files/grest/rpc/epoch/epoch_block_protocols.sql +++ b/files/grest/rpc/epoch/epoch_block_protocols.sql @@ -1,11 +1,11 @@ -CREATE FUNCTION grest.epoch_block_protocols (_epoch_no numeric DEFAULT NULL) - RETURNS TABLE ( - proto_major word31type, - proto_minor word31type, - blocks bigint - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.epoch_block_protocols(_epoch_no numeric DEFAULT NULL) +RETURNS TABLE ( + proto_major word31type, + proto_minor word31type, + blocks bigint +) +LANGUAGE plpgsql +AS $$ BEGIN IF _epoch_no IS NOT NULL THEN RETURN QUERY @@ -26,7 +26,7 @@ BEGIN b.proto_minor, count(b.*) FROM - block b + block AS b WHERE b.epoch_no = (SELECT MAX(no) FROM epoch) GROUP BY @@ -35,4 +35,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.epoch_block_protocols IS 'Get the information about block protocol distribution in epoch'; +COMMENT ON FUNCTION grest.epoch_block_protocols IS 'Get the information about block protocol distribution in epoch'; -- noqa: LT01 diff --git a/files/grest/rpc/epoch/epoch_info.sql b/files/grest/rpc/epoch/epoch_info.sql index 8b0fd95b..da9e5018 100644 --- a/files/grest/rpc/epoch/epoch_info.sql +++ b/files/grest/rpc/epoch/epoch_info.sql @@ -1,24 +1,24 @@ -CREATE OR REPLACE FUNCTION grest.epoch_info (_epoch_no numeric DEFAULT NULL, _include_next_epoch boolean DEFAULT false) - RETURNS TABLE ( - epoch_no word31type, - out_sum text, - fees text, - tx_count word31type, - blk_count word31type, - start_time integer, - end_time integer, - first_block_time integer, - last_block_time integer, - active_stake text, - total_rewards text, - avg_blk_reward text - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.epoch_info(_epoch_no numeric DEFAULT NULL, _include_next_epoch boolean DEFAULT FALSE) +RETURNS TABLE ( + epoch_no word31type, + out_sum text, + fees text, + tx_count word31type, + blk_count word31type, + start_time integer, + end_time integer, + first_block_time integer, + last_block_time integer, + active_stake text, + total_rewards text, + avg_blk_reward text +) +LANGUAGE plpgsql +AS $$ DECLARE - shelley_epoch_duration numeric := (select epochlength::numeric * slotlength::numeric as epochduration from grest.genesis); - shelley_ref_epoch numeric := (select (ep.epoch_no::numeric + 1) from epoch_param ep ORDER BY ep.epoch_no LIMIT 1); - shelley_ref_time numeric := (select ei.i_first_block_time from grest.epoch_info_cache ei where ei.epoch_no = shelley_ref_epoch); + shelley_epoch_duration numeric := (select epochlength::numeric * slotlength::numeric AS epochduration FROM grest.genesis); + shelley_ref_epoch numeric := (select (ep.epoch_no::numeric + 1) FROM epoch_param ep ORDER BY ep.epoch_no LIMIT 1); + shelley_ref_time numeric := (select ei.i_first_block_time FROM grest.epoch_info_cache ei where ei.epoch_no = shelley_ref_epoch); BEGIN RETURN QUERY SELECT @@ -27,15 +27,17 @@ BEGIN ei.i_fees::text AS tx_fees_sum, ei.i_tx_count AS tx_count, ei.i_blk_count AS blk_count, - CASE WHEN ei.epoch_no < shelley_ref_epoch THEN + CASE + WHEN ei.epoch_no < shelley_ref_epoch THEN ei.i_first_block_time::integer - ELSE + ELSE (shelley_ref_time + (ei.epoch_no - shelley_ref_epoch) * shelley_epoch_duration)::integer END AS start_time, - CASE WHEN ei.epoch_no < shelley_ref_epoch THEN - (ei.i_first_block_time + shelley_epoch_duration)::integer - ELSE - (shelley_ref_time + ((ei.epoch_no + 1) - shelley_ref_epoch) * shelley_epoch_duration)::integer + CASE + WHEN ei.epoch_no < shelley_ref_epoch THEN + (ei.i_first_block_time + shelley_epoch_duration)::integer + ELSE + (shelley_ref_time + ((ei.epoch_no + 1) - shelley_ref_epoch) * shelley_epoch_duration)::integer END AS end_time, ei.i_first_block_time::integer AS first_block_time, ei.i_last_block_time::integer AS last_block_time, @@ -43,8 +45,8 @@ BEGIN ei.i_total_rewards::text AS total_rewards, ei.i_avg_blk_reward::text AS avg_blk_reward FROM - grest.epoch_info_cache ei - LEFT JOIN grest.EPOCH_ACTIVE_STAKE_CACHE eas ON eas.epoch_no = ei.epoch_no + grest.epoch_info_cache AS ei + LEFT JOIN grest.epoch_active_stake_cache AS eas ON eas.epoch_no = ei.epoch_no WHERE CASE WHEN _epoch_no IS NULL THEN ei.epoch_no <= (SELECT MAX(epoch.no) FROM public.epoch) @@ -56,4 +58,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.epoch_info IS 'Get the epoch information, all epochs if no epoch specified. If _include_next_epoch is set to true, also return active stake calculation for next epoch if available'; +COMMENT ON FUNCTION grest.epoch_info IS 'Get the epoch information, all epochs if no epoch specified. If _include_next_epoch is set to true, also return active stake calculation for next epoch if available'; -- noqa: LT01 diff --git a/files/grest/rpc/epoch/epoch_params.sql b/files/grest/rpc/epoch/epoch_params.sql index 951789c1..9807a468 100644 --- a/files/grest/rpc/epoch/epoch_params.sql +++ b/files/grest/rpc/epoch/epoch_params.sql @@ -1,39 +1,40 @@ -CREATE FUNCTION grest.epoch_params (_epoch_no numeric DEFAULT NULL) - RETURNS TABLE ( - epoch_no word31type, - min_fee_a word31type, - min_fee_b word31type, - max_block_size word31type, - max_tx_size word31type, - max_bh_size word31type, - key_deposit text, - pool_deposit text, - max_epoch word31type, - optimal_pool_count word31type, - influence double precision, - monetary_expand_rate double precision, - treasury_growth_rate double precision, - decentralisation double precision, - extra_entropy text, - protocol_major word31type, - protocol_minor word31type, - min_utxo_value text, - min_pool_cost text, - nonce text, - block_hash text, - cost_models character varying, - price_mem double precision, - price_step double precision, - max_tx_ex_mem word64type, - max_tx_ex_steps word64type, - max_block_ex_mem word64type, - max_block_ex_steps word64type, - max_val_size word64type, - collateral_percent word31type, - max_collateral_inputs word31type, - coins_per_utxo_size text) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.epoch_params(_epoch_no numeric DEFAULT NULL) +RETURNS TABLE ( + epoch_no word31type, + min_fee_a word31type, + min_fee_b word31type, + max_block_size word31type, + max_tx_size word31type, + max_bh_size word31type, + key_deposit text, + pool_deposit text, + max_epoch word31type, + optimal_pool_count word31type, + influence double precision, + monetary_expand_rate double precision, + treasury_growth_rate double precision, + decentralisation double precision, + extra_entropy text, + protocol_major word31type, + protocol_minor word31type, + min_utxo_value text, + min_pool_cost text, + nonce text, + block_hash text, + cost_models character varying, + price_mem double precision, + price_step double precision, + max_tx_ex_mem word64type, + max_tx_ex_steps word64type, + max_block_ex_mem word64type, + max_block_ex_steps word64type, + max_val_size word64type, + collateral_percent word31type, + max_collateral_inputs word31type, + coins_per_utxo_size text +) +LANGUAGE plpgsql +AS $$ BEGIN IF _epoch_no IS NULL THEN RETURN QUERY @@ -71,7 +72,7 @@ BEGIN ei.p_max_collateral_inputs AS max_collateral_inputs, ei.p_coins_per_utxo_size::text AS coins_per_utxo_size FROM - grest.epoch_info_cache ei + grest.epoch_info_cache AS ei WHERE ei.epoch_no <= (SELECT MAX(epoch.no) FROM public.epoch) ORDER BY @@ -112,12 +113,11 @@ BEGIN ei.p_max_collateral_inputs AS max_collateral_inputs, ei.p_coins_per_utxo_size::text AS coins_per_utxo_size FROM - grest.epoch_info_cache ei + grest.epoch_info_cache AS ei WHERE ei.epoch_no = _epoch_no; END IF; END; $$; -COMMENT ON FUNCTION grest.epoch_params IS 'Get the epoch parameters, all epochs if no epoch specified'; - +COMMENT ON FUNCTION grest.epoch_params IS 'Get the epoch parameters, all epochs if no epoch specified'; -- noqa: LT01 diff --git a/files/grest/rpc/pool/pool_blocks.sql b/files/grest/rpc/pool/pool_blocks.sql index 1f3836d5..372dac8e 100644 --- a/files/grest/rpc/pool/pool_blocks.sql +++ b/files/grest/rpc/pool/pool_blocks.sql @@ -1,36 +1,36 @@ -CREATE FUNCTION grest.pool_blocks (_pool_bech32 text, _epoch_no word31type DEFAULT NULL) - RETURNS TABLE ( - epoch_no word31type, - epoch_slot word31type, - abs_slot word63type, - block_height word31type, - block_hash text, - block_time integer - ) - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.pool_blocks(_pool_bech32 text, _epoch_no word31type DEFAULT NULL) +RETURNS TABLE ( + epoch_no word31type, + epoch_slot word31type, + abs_slot word63type, + block_height word31type, + block_hash text, + block_time integer +) +LANGUAGE plpgsql +AS $$ BEGIN - RETURN query - SELECT - b.epoch_no, - b.epoch_slot_no as epoch_slot, - b.slot_no as abs_slot, - b.block_no as block_height, - encode(b.hash::bytea, 'hex'), - EXTRACT(epoch from b.time)::integer - FROM - public.block b - INNER JOIN - public.slot_leader AS sl ON b.slot_leader_id = sl.id - WHERE - sl.pool_hash_id = (SELECT pool_hash_id FROM grest.pool_info_cache WHERE pool_id_bech32 = _pool_bech32 ORDER BY tx_id DESC LIMIT 1) - AND - ( - _epoch_no IS NULL - OR - b.epoch_no = _epoch_no - ); + RETURN query + SELECT + b.epoch_no, + b.epoch_slot_no AS epoch_slot, + b.slot_no AS abs_slot, + b.block_no AS block_height, + encode(b.hash::bytea, 'hex'), + EXTRACT(EPOCH FROM b.time)::integer + FROM + public.block AS b + INNER JOIN + public.slot_leader AS sl ON b.slot_leader_id = sl.id + WHERE + sl.pool_hash_id = (SELECT pool_hash_id FROM grest.pool_info_cache WHERE pool_id_bech32 = _pool_bech32 ORDER BY tx_id DESC LIMIT 1) + AND + ( + _epoch_no IS NULL + OR + b.epoch_no = _epoch_no + ); END; $$; -COMMENT ON FUNCTION grest.pool_blocks IS 'Return information about blocks minted by a given pool in current epoch (or epoch nbr if provided)'; +COMMENT ON FUNCTION grest.pool_blocks IS 'Return information about blocks minted by a given pool in current epoch (or epoch nbr if provided)'; -- noqa: LT01 diff --git a/files/grest/rpc/pool/pool_delegators.sql b/files/grest/rpc/pool/pool_delegators.sql index 2418b8ca..df1fe119 100644 --- a/files/grest/rpc/pool/pool_delegators.sql +++ b/files/grest/rpc/pool/pool_delegators.sql @@ -1,13 +1,13 @@ -CREATE FUNCTION grest.pool_delegators (_pool_bech32 text) - RETURNS TABLE ( - stake_address character varying, - amount text, - active_epoch_no bigint, - latest_delegation_tx_hash text - ) - LANGUAGE plpgsql - AS $$ - #variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_delegators(_pool_bech32 text) +RETURNS TABLE ( + stake_address character varying, + amount text, + active_epoch_no bigint, + latest_delegation_tx_hash text +) +LANGUAGE plpgsql +AS $$ +#variable_conflict use_column DECLARE _pool_id bigint; BEGIN @@ -15,33 +15,31 @@ BEGIN WITH _all_delegations AS ( SELECT - SA.id AS stake_address_id, - SDC.stake_address, + sa.id AS stake_address_id, + sdc.stake_address, ( - CASE WHEN SDC.total_balance >= 0 - THEN SDC.total_balance + CASE WHEN sdc.total_balance >= 0 + THEN sdc.total_balance ELSE 0 END ) AS total_balance - FROM - grest.stake_distribution_cache AS SDC - INNER JOIN public.stake_address SA ON SA.view = SDC.stake_address + FROM grest.stake_distribution_cache AS sdc + INNER JOIN public.stake_address AS sa ON sa.view = sdc.stake_address WHERE - SDC.pool_id = _pool_bech32 + sdc.pool_id = _pool_bech32 ) - SELECT DISTINCT ON (AD.stake_address) - AD.stake_address, - AD.total_balance::text, - D.active_epoch_no, + SELECT DISTINCT ON (ad.stake_address) + ad.stake_address, + ad.total_balance::text, + d.active_epoch_no, ENCODE(tx.hash, 'hex') - FROM - _all_delegations AS AD - INNER JOIN public.delegation D ON D.addr_id = AD.stake_address_id - INNER JOIN public.tx ON tx.id = D.tx_id + FROM _all_delegations AS ad + INNER JOIN public.delegation AS d ON d.addr_id = ad.stake_address_id + INNER JOIN public.tx ON tx.id = d.tx_id ORDER BY - AD.stake_address, D.tx_id DESC; + ad.stake_address, d.tx_id DESC; END; $$; -COMMENT ON FUNCTION grest.pool_delegators IS 'Return information about live delegators for a given pool.'; +COMMENT ON FUNCTION grest.pool_delegators IS 'Return information about live delegators for a given pool.'; --noqa: LT01 diff --git a/files/grest/rpc/pool/pool_delegators_history.sql b/files/grest/rpc/pool/pool_delegators_history.sql index 4a33e2bd..80ac309e 100644 --- a/files/grest/rpc/pool/pool_delegators_history.sql +++ b/files/grest/rpc/pool/pool_delegators_history.sql @@ -1,47 +1,46 @@ -CREATE FUNCTION grest.pool_delegators_history (_pool_bech32 text, _epoch_no word31type DEFAULT NULL) - RETURNS TABLE ( - stake_address character varying, - amount text, - epoch_no word31type - ) - LANGUAGE plpgsql - AS $$ - #variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_delegators_history(_pool_bech32 text, _epoch_no word31type DEFAULT NULL) +RETURNS TABLE ( + stake_address character varying, + amount text, + epoch_no word31type +) +LANGUAGE plpgsql +AS $$ +#variable_conflict use_column DECLARE _pool_id bigint; BEGIN SELECT id INTO _pool_id FROM pool_hash WHERE pool_hash.view = _pool_bech32; - IF _epoch_no IS NULL THEN RETURN QUERY SELECT - SA.view, - ES.amount::text, - ES.epoch_no + sa.view, + es.amount::text, + es.epoch_no FROM - public.epoch_stake ES - INNER JOIN public.stake_address SA ON ES.addr_id = SA.id + public.epoch_stake AS es + INNER JOIN public.stake_address AS sa ON es.addr_id = sa.id WHERE - ES.pool_id = _pool_id + es.pool_id = _pool_id ORDER BY - ES.epoch_no DESC, ES.amount DESC; + es.epoch_no DESC, es.amount DESC; ELSE RETURN QUERY SELECT - SA.view, - ES.amount::text, - ES.epoch_no + sa.view, + es.amount::text, + es.epoch_no FROM - public.epoch_stake ES - INNER JOIN public.stake_address SA ON ES.addr_id = SA.id + public.epoch_stake AS es + INNER JOIN public.stake_address AS sa ON es.addr_id = sa.id WHERE - ES.pool_id = _pool_id + es.pool_id = _pool_id AND - ES.epoch_no = _epoch_no + es.epoch_no = _epoch_no ORDER BY - ES.amount DESC; + es.amount DESC; END IF; END; $$; -COMMENT ON FUNCTION grest.pool_delegators IS 'Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided.'; +COMMENT ON FUNCTION grest.pool_delegators IS 'Return information about active delegators (incl. history) for a given pool and epoch number - current epoch if not provided.'; --noqa: LT01 diff --git a/files/grest/rpc/pool/pool_history.sql b/files/grest/rpc/pool/pool_history.sql index d00af163..5fe43755 100644 --- a/files/grest/rpc/pool/pool_history.sql +++ b/files/grest/rpc/pool/pool_history.sql @@ -1,36 +1,44 @@ -CREATE OR REPLACE FUNCTION grest.pool_history (_pool_bech32 text, _epoch_no word31type DEFAULT NULL) - RETURNS TABLE ( - epoch_no bigint, - active_stake text, - active_stake_pct numeric, - saturation_pct numeric, - block_cnt bigint, - delegator_cnt bigint, - margin double precision, - fixed_cost text, - pool_fees text, - deleg_rewards text, - member_rewards text, - epoch_ros numeric - ) - LANGUAGE plpgsql - AS $$ - #variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_history(_pool_bech32 text, _epoch_no word31type DEFAULT NULL) +RETURNS TABLE ( + epoch_no bigint, + active_stake text, + active_stake_pct numeric, + saturation_pct numeric, + block_cnt bigint, + delegator_cnt bigint, + margin double precision, + fixed_cost text, + pool_fees text, + deleg_rewards text, + member_rewards text, + epoch_ros numeric +) +LANGUAGE plpgsql +AS $$ +#variable_conflict use_column DECLARE BEGIN - RETURN QUERY - SELECT epoch_no, active_stake::text, active_stake_pct, saturation_pct, block_cnt, - delegator_cnt, pool_fee_variable as margin, coalesce(pool_fee_fixed, 0)::text as fixed_cost, - coalesce(pool_fees, 0)::text, coalesce(deleg_rewards, 0)::text, coalesce(member_rewards, 0)::text, coalesce(epoch_ros, 0) - FROM grest.pool_history_cache phc + SELECT + epoch_no, + active_stake::text, + active_stake_pct, + saturation_pct, + block_cnt, + delegator_cnt, + pool_fee_variable AS margin, + COALESCE(pool_fee_fixed, 0)::text AS fixed_cost, + COALESCE(pool_fees, 0)::text, + COALESCE(deleg_rewards, 0)::text, + COALESCE(member_rewards, 0)::text, + COALESCE(epoch_ros, 0) + FROM grest.pool_history_cache AS phc WHERE phc.pool_id = _pool_bech32 and - (_epoch_no is null or + (_epoch_no IS NULL OR phc.epoch_no = _epoch_no) ORDER by phc.epoch_no desc; - END; $$; -COMMENT ON FUNCTION grest.pool_history IS 'Pool block production and reward history for a given epoch (or all epochs if not specified)'; +COMMENT ON FUNCTION grest.pool_history IS 'Pool block production and reward history for a given epoch (or all epochs if not specified)'; -- noqa: LT01 diff --git a/files/grest/rpc/pool/pool_info.sql b/files/grest/rpc/pool/pool_info.sql index 335f9ae5..fea544df 100644 --- a/files/grest/rpc/pool/pool_info.sql +++ b/files/grest/rpc/pool/pool_info.sql @@ -1,45 +1,43 @@ -CREATE FUNCTION grest.pool_info (_pool_bech32_ids text[]) - RETURNS TABLE ( - pool_id_bech32 character varying, - pool_id_hex text, - active_epoch_no bigint, - vrf_key_hash text, - margin double precision, - fixed_cost text, - pledge text, - reward_addr character varying, - owners character varying [], - relays jsonb [], - meta_url character varying, - meta_hash text, - meta_json jsonb, - pool_status text, - retiring_epoch word31type, - op_cert text, - op_cert_counter word63type, - active_stake text, - sigma numeric, - block_count numeric, - live_pledge text, - live_stake text, - live_delegators bigint, - live_saturation numeric - ) - LANGUAGE plpgsql - AS $$ - #variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_info(_pool_bech32_ids text []) +RETURNS TABLE ( + pool_id_bech32 character varying, + pool_id_hex text, + active_epoch_no bigint, + vrf_key_hash text, + margin double precision, + fixed_cost text, + pledge text, + reward_addr character varying, + owners character varying [], + relays jsonb [], + meta_url character varying, + meta_hash text, + meta_json jsonb, + pool_status text, + retiring_epoch word31type, + op_cert text, + op_cert_counter word63type, + active_stake text, + sigma numeric, + block_count numeric, + live_pledge text, + live_stake text, + live_delegators bigint, + live_saturation numeric +) +LANGUAGE plpgsql +AS $$ +#variable_conflict use_column DECLARE _epoch_no bigint; _saturation_limit bigint; BEGIN SELECT MAX(epoch.no) INTO _epoch_no FROM public.epoch; - SELECT FLOOR(supply::bigint / ( SELECT p_optimal_pool_count FROM grest.epoch_info_cache WHERE epoch_no = _epoch_no ))::bigint INTO _saturation_limit FROM grest.totals(_epoch_no); - RETURN QUERY WITH _all_pool_info AS ( @@ -82,8 +80,7 @@ BEGIN _all_pool_info AS api LEFT JOIN LATERAL ( ( - SELECT - pod.json + SELECT pod.json FROM public.pool_offline_data AS pod WHERE @@ -93,8 +90,7 @@ BEGIN ) UNION ALL ( - SELECT - pod.json + SELECT pod.json FROM public.pool_offline_data AS pod WHERE @@ -125,8 +121,7 @@ BEGIN LIMIT 1 ) block_data ON TRUE LEFT JOIN LATERAL( - SELECT - amount::lovelace AS as_sum + SELECT amount::lovelace AS as_sum FROM grest.pool_active_stake_cache AS pasc WHERE @@ -135,8 +130,7 @@ BEGIN pasc.epoch_no = _epoch_no ) active_stake ON TRUE LEFT JOIN LATERAL( - SELECT - amount::lovelace AS es_sum + SELECT amount::lovelace AS es_sum FROM grest.epoch_active_stake_cache AS easc WHERE @@ -147,18 +141,18 @@ BEGIN CASE WHEN api.pool_status = 'retired' THEN NULL ELSE - SUM ( + SUM( CASE WHEN total_balance >= 0 THEN total_balance ELSE 0 END )::lovelace END AS stake, - COUNT (stake_address) AS delegators, + COUNT(stake_address) AS delegators, CASE WHEN api.pool_status = 'retired' THEN NULL ELSE - SUM (CASE WHEN sdc.stake_address = ANY (api.owners) THEN total_balance ELSE 0 END)::lovelace + SUM(CASE WHEN sdc.stake_address = ANY(api.owners) THEN total_balance ELSE 0 END)::lovelace END AS pledge FROM grest.stake_distribution_cache AS sdc @@ -168,4 +162,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.pool_info IS 'Current pool status and details for a specified list of pool ids'; +COMMENT ON FUNCTION grest.pool_info IS 'Current pool status and details for a specified list of pool ids'; --noqa: LT01 diff --git a/files/grest/rpc/pool/pool_list.sql b/files/grest/rpc/pool/pool_list.sql index 3bb9ddbd..c656d274 100644 --- a/files/grest/rpc/pool/pool_list.sql +++ b/files/grest/rpc/pool/pool_list.sql @@ -1,12 +1,12 @@ -CREATE OR REPLACE FUNCTION grest.pool_list () - RETURNS TABLE ( - pool_id_bech32 character varying, - ticker character varying) - LANGUAGE plpgsql - AS $$ - # variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_list() +RETURNS TABLE ( + pool_id_bech32 character varying, + ticker character varying +) +LANGUAGE plpgsql +AS $$ +# variable_conflict use_column BEGIN - RETURN QUERY ( WITH -- Get last pool update for each pool @@ -20,6 +20,7 @@ BEGIN pic.pool_id_bech32, pic.tx_id DESC ), + _pool_meta AS ( SELECT DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, @@ -41,8 +42,6 @@ BEGIN LEFT JOIN _pool_meta AS pm ON pl.pool_id_bech32 = pm.pool_id_bech32 WHERE pool_status != 'retired' - ); - END; $$; diff --git a/files/grest/rpc/pool/pool_metadata.sql b/files/grest/rpc/pool/pool_metadata.sql index 103fda50..872baf18 100644 --- a/files/grest/rpc/pool/pool_metadata.sql +++ b/files/grest/rpc/pool/pool_metadata.sql @@ -1,34 +1,34 @@ -CREATE FUNCTION grest.pool_metadata (_pool_bech32_ids text[] DEFAULT null) - RETURNS TABLE ( - pool_id_bech32 character varying, - meta_url character varying, - meta_hash text, - meta_json jsonb - ) - LANGUAGE plpgsql - AS $$ - #variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_metadata(_pool_bech32_ids text [] DEFAULT null) +RETURNS TABLE ( + pool_id_bech32 character varying, + meta_url character varying, + meta_hash text, + meta_json jsonb +) +LANGUAGE plpgsql +AS $$ +#variable_conflict use_column BEGIN - RETURN QUERY - SELECT - DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, - pic.meta_url, - pic.meta_hash, - pod.json - FROM - grest.pool_info_cache AS pic - LEFT JOIN - public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id - WHERE - pic.pool_status != 'retired' - AND - CASE - WHEN _pool_bech32_ids IS NULL THEN true - WHEN _pool_bech32_ids IS NOT NULL THEN pic.pool_id_bech32 = ANY(SELECT UNNEST(_pool_bech32_ids)) - END - ORDER BY - pic.pool_id_bech32, pic.tx_id DESC; + RETURN QUERY + SELECT + DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, + pic.meta_url, + pic.meta_hash, + pod.json + FROM + grest.pool_info_cache AS pic + LEFT JOIN + public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id + WHERE + pic.pool_status != 'retired' + AND + CASE + WHEN _pool_bech32_ids IS NULL THEN TRUE + WHEN _pool_bech32_ids IS NOT NULL THEN pic.pool_id_bech32 = ANY(SELECT UNNEST(_pool_bech32_ids)) + END + ORDER BY + pic.pool_id_bech32, pic.tx_id DESC; END; $$; -COMMENT ON FUNCTION grest.pool_metadata IS 'Metadata(on & off-chain) for all currently registered/retiring (not retired) pools'; +COMMENT ON FUNCTION grest.pool_metadata IS 'Metadata(on & off-chain) for all currently registered/retiring (not retired) pools'; -- noqa: LT01 diff --git a/files/grest/rpc/pool/pool_relays.sql b/files/grest/rpc/pool/pool_relays.sql index 43b95e4f..29fd8c28 100644 --- a/files/grest/rpc/pool/pool_relays.sql +++ b/files/grest/rpc/pool/pool_relays.sql @@ -1,23 +1,23 @@ -CREATE FUNCTION grest.pool_relays () - RETURNS TABLE ( - pool_id_bech32 character varying, - relays jsonb [] - ) - LANGUAGE plpgsql - AS $$ - #variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_relays() +RETURNS TABLE ( + pool_id_bech32 character varying, + relays jsonb [] +) +LANGUAGE plpgsql +AS $$ +#variable_conflict use_column BEGIN - RETURN QUERY - SELECT - DISTINCT ON (pool_id_bech32) pool_id_bech32, - relays - FROM - grest.pool_info_cache - WHERE - pool_status != 'retired' - ORDER BY - pool_id_bech32, tx_id DESC; + RETURN QUERY + SELECT + DISTINCT ON (pool_id_bech32) pool_id_bech32, + relays + FROM + grest.pool_info_cache + WHERE + pool_status != 'retired' + ORDER BY + pool_id_bech32, tx_id DESC; END; $$; -COMMENT ON FUNCTION grest.pool_relays IS 'A list of registered relays for all currently registered/retiring (not retired) pools'; +COMMENT ON FUNCTION grest.pool_relays IS 'A list of registered relays for all currently registered/retiring (not retired) pools'; --noqa: LT01 diff --git a/files/grest/rpc/pool/pool_stake_snapshot.sql b/files/grest/rpc/pool/pool_stake_snapshot.sql index 643fea05..73a22f21 100644 --- a/files/grest/rpc/pool/pool_stake_snapshot.sql +++ b/files/grest/rpc/pool/pool_stake_snapshot.sql @@ -1,13 +1,13 @@ -CREATE FUNCTION grest.pool_stake_snapshot (_pool_bech32 text) - RETURNS TABLE ( - snapshot text, - epoch_no bigint, - nonce text, - pool_stake text, - active_stake text - ) - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.pool_stake_snapshot(_pool_bech32 text) +RETURNS TABLE ( + snapshot text, + epoch_no bigint, + nonce text, + pool_stake text, + active_stake text +) +LANGUAGE plpgsql +AS $$ DECLARE _epoch_no bigint; _mark bigint; @@ -31,9 +31,9 @@ BEGIN pasc.amount::text, easc.amount::text FROM - grest.pool_active_stake_cache pasc - INNER JOIN grest.epoch_active_stake_cache easc ON easc.epoch_no = pasc.epoch_no - LEFT JOIN grest.epoch_info_cache eic ON eic.epoch_no = pasc.epoch_no + grest.pool_active_stake_cache AS pasc + INNER JOIN grest.epoch_active_stake_cache AS easc ON easc.epoch_no = pasc.epoch_no + LEFT JOIN grest.epoch_info_cache AS eic ON eic.epoch_no = pasc.epoch_no WHERE pasc.pool_id = _pool_bech32 AND pasc.epoch_no BETWEEN _go AND _mark @@ -42,4 +42,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.pool_stake_snapshot IS 'Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation'; \ No newline at end of file +COMMENT ON FUNCTION grest.pool_stake_snapshot IS 'Returns Mark, Set and Go stake snapshots for the selected pool, useful for leaderlog calculation'; -- noqa: LT01 diff --git a/files/grest/rpc/pool/pool_updates.sql b/files/grest/rpc/pool/pool_updates.sql index 52960bd1..ac4ca722 100644 --- a/files/grest/rpc/pool/pool_updates.sql +++ b/files/grest/rpc/pool/pool_updates.sql @@ -1,56 +1,56 @@ -CREATE FUNCTION grest.pool_updates (_pool_bech32 text DEFAULT NULL) - RETURNS TABLE ( - tx_hash text, - block_time integer, - pool_id_bech32 character varying, - pool_id_hex text, - active_epoch_no bigint, - vrf_key_hash text, - margin double precision, - fixed_cost text, - pledge text, - reward_addr character varying, - owners character varying [], - relays jsonb [], - meta_url character varying, - meta_hash text, - meta_json jsonb, - pool_status text, - retiring_epoch word31type - ) - LANGUAGE plpgsql - AS $$ - #variable_conflict use_column +CREATE OR REPLACE FUNCTION grest.pool_updates(_pool_bech32 text DEFAULT NULL) +RETURNS TABLE ( + tx_hash text, + block_time integer, + pool_id_bech32 character varying, + pool_id_hex text, + active_epoch_no bigint, + vrf_key_hash text, + margin double precision, + fixed_cost text, + pledge text, + reward_addr character varying, + owners character varying [], + relays jsonb [], + meta_url character varying, + meta_hash text, + meta_json jsonb, + pool_status text, + retiring_epoch word31type +) +LANGUAGE plpgsql +AS $$ +#variable_conflict use_column BEGIN - RETURN QUERY - SELECT - tx_hash, - block_time::integer, - pool_id_bech32, - pool_id_hex, - active_epoch_no, - vrf_key_hash, - margin, - fixed_cost::text, - pledge::text, - reward_addr, - owners, - relays, - meta_url, - meta_hash, - pod.json, - pool_status, - retiring_epoch - FROM - grest.pool_info_cache pic - LEFT JOIN public.pool_offline_data pod ON pod.pmr_id = pic.meta_id - WHERE - _pool_bech32 IS NULL - OR - pool_id_bech32 = _pool_bech32 - ORDER BY - tx_id DESC; + RETURN QUERY + SELECT + tx_hash, + block_time::integer, + pool_id_bech32, + pool_id_hex, + active_epoch_no, + vrf_key_hash, + margin, + fixed_cost::text, + pledge::text, + reward_addr, + owners, + relays, + meta_url, + meta_hash, + pod.json, + pool_status, + retiring_epoch + FROM + grest.pool_info_cache AS pic + LEFT JOIN public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id + WHERE + _pool_bech32 IS NULL + OR + pool_id_bech32 = _pool_bech32 + ORDER BY + tx_id DESC; END; $$; -COMMENT ON FUNCTION grest.pool_updates IS 'Return all pool_updates for all pools or only updates for specific pool if specified'; +COMMENT ON FUNCTION grest.pool_updates IS 'Return all pool_updates for all pools or only updates for specific pool if specified'; -- noqa: LT01 diff --git a/files/grest/rpc/script/datum_info.sql b/files/grest/rpc/script/datum_info.sql index bf652e73..663a1913 100644 --- a/files/grest/rpc/script/datum_info.sql +++ b/files/grest/rpc/script/datum_info.sql @@ -1,28 +1,26 @@ -CREATE FUNCTION grest.datum_info (_datum_hashes text[]) - RETURNS TABLE ( - hash text, - value jsonb, - bytes text - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.datum_info(_datum_hashes text []) +RETURNS TABLE ( + hash text, + value jsonb, + bytes text +) +LANGUAGE plpgsql +AS $$ DECLARE _datum_hashes_decoded bytea[]; BEGIN - SELECT INTO _datum_hashes_decoded - ARRAY_AGG(DECODE(d_hash, 'hex')) + SELECT INTO _datum_hashes_decoded ARRAY_AGG(DECODE(d_hash, 'hex')) FROM UNNEST(_datum_hashes) AS d_hash; - RETURN QUERY SELECT ENCODE(d.hash, 'hex'), d.value, ENCODE(d.bytes, 'hex') FROM - datum d + datum AS d WHERE d.hash = ANY(_datum_hashes_decoded); END; $$; -COMMENT ON FUNCTION grest.datum_info IS 'Get information about a given data from hashes.'; +COMMENT ON FUNCTION grest.datum_info IS 'Get information about a given data FROM hashes.'; -- noqa: LT01 diff --git a/files/grest/rpc/script/native_script_list.sql b/files/grest/rpc/script/native_script_list.sql index a7f9eac3..a0af9728 100644 --- a/files/grest/rpc/script/native_script_list.sql +++ b/files/grest/rpc/script/native_script_list.sql @@ -1,16 +1,16 @@ -CREATE FUNCTION grest.native_script_list () - RETURNS TABLE ( - script_hash text, - creation_tx_hash text, - type scripttype, - script jsonb - ) -LANGUAGE PLPGSQL AS -$$ +CREATE OR REPLACE FUNCTION grest.native_script_list() +RETURNS TABLE ( + script_hash text, + creation_tx_hash text, + type scripttype, + script jsonb +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT - ENCODE(script.hash, 'hex'), + ENCODE(script.hash, 'hex'), ENCODE(tx.hash, 'hex'), script.type, script.json @@ -20,4 +20,4 @@ BEGIN END; $$; -COMMENT ON FUNCTION grest.native_script_list IS 'Get a list of all native(multisig/timelock) script hashes with creation tx hash, type and script in json format.'; +COMMENT ON FUNCTION grest.native_script_list IS 'Get a list of all native(multisig/timelock) script hashes with creation tx hash, type and script in json format.'; --noqa: LT01 diff --git a/files/grest/rpc/script/plutus_script_list.sql b/files/grest/rpc/script/plutus_script_list.sql index 63d05717..e149f5d8 100644 --- a/files/grest/rpc/script/plutus_script_list.sql +++ b/files/grest/rpc/script/plutus_script_list.sql @@ -1,19 +1,19 @@ -CREATE FUNCTION grest.plutus_script_list () - RETURNS TABLE ( - script_hash text, - creation_tx_hash text - ) -LANGUAGE PLPGSQL AS -$$ +CREATE OR REPLACE FUNCTION grest.plutus_script_list() +RETURNS TABLE ( + script_hash text, + creation_tx_hash text +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT - ENCODE(script.hash, 'hex') as script_hash, - ENCODE(tx.hash, 'hex') as creation_tx_hash + ENCODE(script.hash, 'hex') AS script_hash, + ENCODE(tx.hash, 'hex') AS creation_tx_hash FROM script INNER JOIN tx ON tx.id = script.tx_id WHERE script.type IN ('plutusV1', 'plutusV2'); END; $$; -COMMENT ON FUNCTION grest.plutus_script_list IS 'Get a list of all plutus script hashes with creation tx hash.'; +COMMENT ON FUNCTION grest.plutus_script_list IS 'Get a list of all plutus script hashes with creation tx hash.'; --noqa: LT01 diff --git a/files/grest/rpc/script/script_redeemers.sql b/files/grest/rpc/script/script_redeemers.sql index 546b8f88..19321e45 100644 --- a/files/grest/rpc/script/script_redeemers.sql +++ b/files/grest/rpc/script/script_redeemers.sql @@ -1,42 +1,35 @@ -CREATE FUNCTION grest.script_redeemers (_script_hash text) - RETURNS TABLE ( - script_hash text, - redeemers jsonb - ) -LANGUAGE PLPGSQL AS -$$ +CREATE OR REPLACE FUNCTION grest.script_redeemers(_script_hash text) +RETURNS TABLE ( + script_hash text, + redeemers jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE _script_hash_bytea bytea; BEGIN -SELECT INTO _script_hash_bytea DECODE(_script_hash, 'hex'); -RETURN QUERY -select _script_hash, + SELECT INTO _script_hash_bytea DECODE(_script_hash, 'hex'); + RETURN QUERY + SELECT + _script_hash, JSONB_AGG( - JSONB_BUILD_OBJECT( - 'tx_hash', - ENCODE(tx.hash, 'hex'), - 'tx_index', - redeemer.index, - 'unit_mem', - redeemer.unit_mem, - 'unit_steps', - redeemer.unit_steps, - 'fee', - redeemer.fee::text, - 'purpose', - redeemer.purpose, - 'datum_hash', - ENCODE(rd.hash, 'hex'), - 'datum_value', - rd.value - -- extra bytes field available in rd. table here - ) - ) as redeemers -FROM redeemer - INNER JOIN TX ON tx.id = redeemer.tx_id - INNER JOIN REDEEMER_DATA rd on rd.id = redeemer.redeemer_data_id -WHERE redeemer.script_hash = _script_hash_bytea -GROUP BY redeemer.script_hash; + JSONB_BUILD_OBJECT( + 'tx_hash', ENCODE(tx.hash, 'hex'), + 'tx_index', redeemer.index, + 'unit_mem', redeemer.unit_mem, + 'unit_steps', redeemer.unit_steps, + 'fee', redeemer.fee::text, + 'purpose', redeemer.purpose, + 'datum_hash', ENCODE(rd.hash, 'hex'), + 'datum_value', rd.value + -- extra bytes field available in rd. table here + ) + ) AS redeemers + FROM redeemer + INNER JOIN TX ON tx.id = redeemer.tx_id + INNER JOIN REDEEMER_DATA rd ON rd.id = redeemer.redeemer_data_id + WHERE redeemer.script_hash = _script_hash_bytea + GROUP BY redeemer.script_hash; END; $$; -COMMENT ON FUNCTION grest.script_redeemers IS 'Get all redeemers for a given script hash.'; +COMMENT ON FUNCTION grest.script_redeemers IS 'Get all redeemers for a given script hash.'; --noqa: LT01 diff --git a/files/grest/rpc/transactions/tx_info.sql b/files/grest/rpc/transactions/tx_info.sql index a24fd3bc..7719947c 100644 --- a/files/grest/rpc/transactions/tx_info.sql +++ b/files/grest/rpc/transactions/tx_info.sql @@ -1,114 +1,107 @@ -CREATE FUNCTION grest.tx_info (_tx_hashes text[]) - RETURNS TABLE ( - tx_hash text, - block_hash text, - block_height word31type, - epoch_no word31type, - epoch_slot word31type, - absolute_slot word63type, - tx_timestamp integer, - tx_block_index word31type, - tx_size word31type, - total_output text, - fee text, - deposit text, - invalid_before text, - invalid_after text, - collateral_inputs jsonb, - collateral_output jsonb, - reference_inputs jsonb, - inputs jsonb, - outputs jsonb, - withdrawals jsonb, - assets_minted jsonb, - metadata jsonb, - certificates jsonb, - native_scripts jsonb, - plutus_contracts jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.tx_info(_tx_hashes text []) +RETURNS TABLE ( + tx_hash text, + block_hash text, + block_height word31type, + epoch_no word31type, + epoch_slot word31type, + absolute_slot word63type, + tx_timestamp integer, + tx_block_index word31type, + tx_size word31type, + total_output text, + fee text, + deposit text, + invalid_before text, + invalid_after text, + collateral_inputs jsonb, + collateral_output jsonb, + reference_inputs jsonb, + inputs jsonb, + outputs jsonb, + withdrawals jsonb, + assets_minted jsonb, + metadata jsonb, + certificates jsonb, + native_scripts jsonb, + plutus_contracts jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE _tx_hashes_bytea bytea[]; _tx_id_list bigint[]; BEGIN - -- convert input _tx_hashes array into bytea array SELECT INTO _tx_hashes_bytea ARRAY_AGG(hashes_bytea) FROM ( - SELECT - DECODE(hashes_hex, 'hex') AS hashes_bytea - FROM - UNNEST(_tx_hashes) AS hashes_hex + SELECT DECODE(hashes_hex, 'hex') AS hashes_bytea + FROM UNNEST(_tx_hashes) AS hashes_hex ) AS tmp; - -- all tx ids SELECT INTO _tx_id_list ARRAY_AGG(id) FROM ( - SELECT - id - FROM - tx - WHERE tx.hash = ANY (_tx_hashes_bytea) + SELECT id + FROM tx + WHERE tx.hash = ANY(_tx_hashes_bytea) ) AS tmp; RETURN QUERY ( WITH - -- limit by last known block, also join with block only once _all_tx AS ( SELECT tx.id, - tx.hash AS tx_hash, - b.hash AS block_hash, - b.block_no AS block_height, - b.epoch_no AS epoch_no, - b.epoch_slot_no AS epoch_slot, - b.slot_no AS absolute_slot, - b.time AS tx_timestamp, - tx.block_index AS tx_block_index, - tx.size AS tx_size, - tx.out_sum AS total_output, + tx.hash AS tx_hash, + b.hash AS block_hash, + b.block_no AS block_height, + b.epoch_no AS epoch_no, + b.epoch_slot_no AS epoch_slot, + b.slot_no AS absolute_slot, + b.time AS tx_timestamp, + tx.block_index AS tx_block_index, + tx.size AS tx_size, + tx.out_sum AS total_output, tx.fee, tx.deposit, tx.invalid_before, - tx.invalid_hereafter AS invalid_after + tx.invalid_hereafter AS invalid_after FROM tx - INNER JOIN block b ON tx.block_id = b.id - WHERE tx.id = ANY (_tx_id_list) + INNER JOIN block AS b ON tx.block_id = b.id + WHERE tx.id = ANY(_tx_id_list) ), _all_collateral_inputs AS ( SELECT - collateral_tx_in.tx_in_id AS tx_id, - tx_out.address AS payment_addr_bech32, - ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, - SA.view AS stake_addr, - ENCODE(tx.hash, 'hex') AS tx_hash, - tx_out.index AS tx_index, - tx_out.value::text AS value, - ENCODE(tx_out.data_hash, 'hex') AS datum_hash, - ( CASE WHEN MA.policy IS NULL THEN NULL + collateral_tx_in.tx_in_id AS tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + sa.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', aic.decimals, - 'quantity', MTO.quantity::text + 'quantity', mto.quantity::text ) END - ) AS asset_list, - ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ) AS asset_list, + (CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'bytes', ENCODE(datum.bytes, 'hex'), 'value', datum.value ) END - ) AS inline_datum, - ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ) AS inline_datum, + (CASE WHEN tx_out.reference_script_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'hash', ENCODE(script.hash, 'hex'), @@ -118,52 +111,52 @@ BEGIN 'size', script.serialised_size ) END - ) AS reference_script + ) AS reference_script FROM collateral_tx_in INNER JOIN tx_out ON tx_out.tx_id = collateral_tx_in.tx_out_id AND tx_out.index = collateral_tx_in.tx_out_index INNER JOIN tx ON tx_out.tx_id = tx.id - LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id - LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id - LEFT JOIN multi_asset MA ON MA.id = MTO.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id - WHERE - collateral_tx_in.tx_in_id = ANY (_tx_id_list) + WHERE + collateral_tx_in.tx_in_id = ANY(_tx_id_list) ), _all_reference_inputs AS ( SELECT - reference_tx_in.tx_in_id AS tx_id, - tx_out.address AS payment_addr_bech32, - ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, - SA.view AS stake_addr, - ENCODE(tx.hash, 'hex') AS tx_hash, - tx_out.index AS tx_index, - tx_out.value::text AS value, - ENCODE(tx_out.data_hash, 'hex') AS datum_hash, - ( CASE WHEN MA.policy IS NULL THEN NULL + reference_tx_in.tx_in_id AS tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + sa.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', aic.decimals, - 'quantity', MTO.quantity::text + 'quantity', mto.quantity::text ) END - ) AS asset_list, - ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ) AS asset_list, + (CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'bytes', ENCODE(datum.bytes, 'hex'), 'value', datum.value ) END - ) AS inline_datum, - ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ) AS inline_datum, + (CASE WHEN tx_out.reference_script_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'hash', ENCODE(script.hash, 'hex'), @@ -173,52 +166,52 @@ BEGIN 'size', script.serialised_size ) END - ) AS reference_script + ) AS reference_script FROM reference_tx_in INNER JOIN tx_out ON tx_out.tx_id = reference_tx_in.tx_out_id AND tx_out.index = reference_tx_in.tx_out_index INNER JOIN tx ON tx_out.tx_id = tx.id - LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id - LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id - LEFT JOIN multi_asset MA ON MA.id = MTO.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id - WHERE - reference_tx_in.tx_in_id = ANY (_tx_id_list) + WHERE + reference_tx_in.tx_in_id = ANY(_tx_id_list) ), _all_inputs AS ( SELECT - tx_in.tx_in_id AS tx_id, - tx_out.address AS payment_addr_bech32, - ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, - SA.view AS stake_addr, - ENCODE(tx.hash, 'hex') AS tx_hash, - tx_out.index AS tx_index, - tx_out.value::text AS value, - ENCODE(tx_out.data_hash, 'hex') AS datum_hash, - ( CASE WHEN MA.policy IS NULL THEN NULL + tx_in.tx_in_id AS tx_id, + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + sa.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', aic.decimals, - 'quantity', MTO.quantity::text + 'quantity', mto.quantity::text ) END - ) AS asset_list, - ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ) AS asset_list, + (CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'bytes', ENCODE(datum.bytes, 'hex'), 'value', datum.value ) END - ) AS inline_datum, - ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ) AS inline_datum, + (CASE WHEN tx_out.reference_script_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'hash', ENCODE(script.hash, 'hex'), @@ -228,52 +221,52 @@ BEGIN 'size', script.serialised_size ) END - ) AS reference_script + ) AS reference_script FROM tx_in INNER JOIN tx_out ON tx_out.tx_id = tx_in.tx_out_id AND tx_out.index = tx_in.tx_out_index - INNER JOIN tx on tx_out.tx_id = tx.id - LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id - LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id - LEFT JOIN multi_asset MA ON MA.id = MTO.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id - WHERE - tx_in.tx_in_id = ANY (_tx_id_list) + WHERE + tx_in.tx_in_id = ANY(_tx_id_list) ), _all_collateral_outputs AS ( SELECT tx_out.tx_id, - tx_out.address AS payment_addr_bech32, - ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, - SA.view AS stake_addr, - ENCODE(tx.hash, 'hex') AS tx_hash, - tx_out.index AS tx_index, - tx_out.value::text AS value, - ENCODE(tx_out.data_hash, 'hex') AS datum_hash, - ( CASE WHEN MA.policy IS NULL THEN NULL + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + sa.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', aic.decimals, - 'quantity', MTO.quantity::text + 'quantity', mto.quantity::text ) END - ) AS asset_list, - ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ) AS asset_list, + (CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'bytes', ENCODE(datum.bytes, 'hex'), 'value', datum.value ) END - ) AS inline_datum, - ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ) AS inline_datum, + (CASE WHEN tx_out.reference_script_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'hash', ENCODE(script.hash, 'hex'), @@ -283,50 +276,50 @@ BEGIN 'size', script.serialised_size ) END - ) AS reference_script + ) AS reference_script FROM - collateral_tx_out tx_out - INNER JOIN tx on tx_out.tx_id = tx.id - LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id - LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id - LEFT JOIN multi_asset MA ON MA.id = MTO.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + collateral_tx_out AS tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id - WHERE - tx_out.tx_id = ANY (_tx_id_list) + WHERE + tx_out.tx_id = ANY(_tx_id_list) ), _all_outputs AS ( SELECT tx_out.tx_id, - tx_out.address AS payment_addr_bech32, - ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, - SA.view AS stake_addr, - ENCODE(tx.hash, 'hex') AS tx_hash, - tx_out.index AS tx_index, - tx_out.value::text AS value, - ENCODE(tx_out.data_hash, 'hex') AS datum_hash, - ( CASE WHEN MA.policy IS NULL THEN NULL + tx_out.address AS payment_addr_bech32, + ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, + sa.view AS stake_addr, + ENCODE(tx.hash, 'hex') AS tx_hash, + tx_out.index AS tx_index, + tx_out.value::text AS value, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', aic.decimals, - 'quantity', MTO.quantity::text + 'quantity', mto.quantity::text ) END - ) AS asset_list, - ( CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL + ) AS asset_list, + (CASE WHEN tx_out.inline_datum_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'bytes', ENCODE(datum.bytes, 'hex'), 'value', datum.value ) END - ) AS inline_datum, - ( CASE WHEN tx_out.reference_script_id IS NULL THEN NULL + ) AS inline_datum, + (CASE WHEN tx_out.reference_script_id IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'hash', ENCODE(script.hash, 'hex'), @@ -336,18 +329,18 @@ BEGIN 'size', script.serialised_size ) END - ) AS reference_script + ) AS reference_script FROM tx_out INNER JOIN tx ON tx_out.tx_id = tx.id - LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id - LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id - LEFT JOIN multi_asset MA ON MA.id = MTO.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id LEFT JOIN script ON script.id = tx_out.reference_script_id - WHERE - tx_out.tx_id = ANY (_tx_id_list) + WHERE + tx_out.tx_id = ANY(_tx_id_list) ), _all_withdrawals AS ( @@ -356,18 +349,17 @@ BEGIN JSONB_AGG(data) AS list FROM ( SELECT - W.tx_id, + w.tx_id, JSONB_BUILD_OBJECT( - 'amount', W.amount::text, - 'stake_addr', SA.view + 'amount', w.amount::text, + 'stake_addr', sa.view ) AS data - FROM - withdrawal W - INNER JOIN stake_address SA ON W.addr_id = SA.id + FROM + withdrawal AS w + INNER JOIN stake_address AS sa ON w.addr_id = sa.id WHERE - W.tx_id = ANY (_tx_id_list) + w.tx_id = ANY(_tx_id_list) ) AS tmp - GROUP BY tx_id ), @@ -377,22 +369,21 @@ BEGIN JSONB_AGG(data) AS list FROM ( SELECT - MTM.tx_id, + mtm.tx_id, JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', COALESCE(aic.decimals, 0), - 'quantity', MTM.quantity::text + 'quantity', mtm.quantity::text ) AS data - FROM - ma_tx_mint MTM - INNER JOIN MULTI_ASSET MA ON MA.id = MTM.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + FROM + ma_tx_mint AS mtm + INNER JOIN multi_asset AS ma ON ma.id = mtm.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id WHERE - MTM.tx_id = ANY (_tx_id_list) + mtm.tx_id = ANY(_tx_id_list) ) AS tmp - GROUP BY tx_id ), @@ -403,10 +394,10 @@ BEGIN tm.key::text, tm.json ) AS list - FROM - tx_metadata TM + FROM + tx_metadata AS tm WHERE - TM.tx_id = ANY (_tx_id_list) + tm.tx_id = ANY(_tx_id_list) GROUP BY tx_id ), @@ -416,204 +407,203 @@ BEGIN JSONB_AGG(data) AS list FROM ( SELECT - SR.tx_id, + sr.tx_id, JSONB_BUILD_OBJECT( - 'index', SR.cert_index, + 'index', sr.cert_index, 'type', 'stake_registration', 'info', JSONB_BUILD_OBJECT( - 'stake_address', SA.view + 'stake_address', sa.view ) ) AS data - FROM - public.stake_registration SR - INNER JOIN public.stake_address SA ON SA.id = SR.addr_id + FROM + public.stake_registration AS sr + INNER JOIN public.stake_address AS sa ON sa.id = sr.addr_id WHERE - SR.tx_id = ANY (_tx_id_list) + sr.tx_id = ANY(_tx_id_list) -- UNION ALL -- SELECT - SD.tx_id, + sd.tx_id, JSONB_BUILD_OBJECT( - 'index', SD.cert_index, + 'index', sd.cert_index, 'type', 'stake_deregistration', 'info', JSONB_BUILD_OBJECT( - 'stake_address', SA.view + 'stake_address', sa.view ) ) AS data - FROM - public.stake_deregistration SD - INNER JOIN public.stake_address SA ON SA.id = SD.addr_id + FROM + public.stake_deregistration AS sd + INNER JOIN public.stake_address AS sa ON sa.id = sd.addr_id WHERE - SD.tx_id = ANY (_tx_id_list) + sd.tx_id = ANY(_tx_id_list) -- UNION ALL -- SELECT - D.tx_id, + d.tx_id, JSONB_BUILD_OBJECT( - 'index', D.cert_index, + 'index', d.cert_index, 'type', 'delegation', 'info', JSONB_BUILD_OBJECT( - 'stake_address', SA.view, - 'pool_id_bech32', PH.view, - 'pool_id_hex', ENCODE(PH.hash_raw, 'hex') + 'stake_address', sa.view, + 'pool_id_bech32', ph.view, + 'pool_id_hex', ENCODE(ph.hash_raw, 'hex') ) ) AS data - FROM - public.delegation D - INNER JOIN public.stake_address SA ON SA.id = D.addr_id - INNER JOIN public.pool_hash PH ON PH.id = D.pool_hash_id + FROM + public.delegation AS d + INNER JOIN public.stake_address AS sa ON sa.id = d.addr_id + INNER JOIN public.pool_hash AS ph ON ph.id = d.pool_hash_id WHERE - D.tx_id = ANY (_tx_id_list) + d.tx_id = ANY(_tx_id_list) -- UNION ALL -- SELECT - T.tx_id, + t.tx_id, JSONB_BUILD_OBJECT( - 'index', T.cert_index, + 'index', t.cert_index, 'type', 'treasury_MIR', 'info', JSONB_BUILD_OBJECT( - 'stake_address', SA.view, - 'amount', T.amount::text + 'stake_address', sa.view, + 'amount', t.amount::text ) ) AS data - FROM - public.treasury T - INNER JOIN public.stake_address SA ON SA.id = T.addr_id + FROM + public.treasury AS t + INNER JOIN public.stake_address AS sa ON sa.id = t.addr_id WHERE - T.tx_id = ANY (_tx_id_list) + t.tx_id = ANY(_tx_id_list) -- UNION ALL -- SELECT - R.tx_id, + r.tx_id, JSONB_BUILD_OBJECT( - 'index', R.cert_index, + 'index', r.cert_index, 'type', 'reserve_MIR', 'info', JSONB_BUILD_OBJECT( - 'stake_address', SA.view, - 'amount', R.amount::text + 'stake_address', sa.view, + 'amount', r.amount::text ) ) AS data - FROM - public.reserve R - INNER JOIN public.stake_address SA ON SA.id = R.addr_id + FROM + public.reserve AS r + INNER JOIN public.stake_address AS sa ON sa.id = r.addr_id WHERE - R.tx_id = ANY (_tx_id_list) + r.tx_id = ANY(_tx_id_list) -- UNION ALL -- SELECT - PT.tx_id, + pt.tx_id, JSONB_BUILD_OBJECT( - 'index', PT.cert_index, + 'index', pt.cert_index, 'type', 'pot_transfer', 'info', JSONB_BUILD_OBJECT( - 'treasury', PT.treasury::text, - 'reserves', PT.reserves::text + 'treasury', pt.treasury::text, + 'reserves', pt.reserves::text ) ) AS data FROM - public.pot_transfer PT + public.pot_transfer AS pt WHERE - PT.tx_id = ANY (_tx_id_list) + pt.tx_id = ANY(_tx_id_list) -- UNION ALL -- - SELECT + SELECT DISTINCT ON (pp.registered_tx_id) -- SELECT DISTINCT below because there are multiple entries for each signing key of a given transaction - DISTINCT ON (PP.registered_tx_id) PP.registered_tx_id AS tx_id, + pp.registered_tx_id AS tx_id, JSONB_BUILD_OBJECT( - 'index', null, -- cert_index not stored in param_proposal table + 'index', NULL, -- cert_index not stored in param_proposal table 'type', 'param_proposal', 'info', JSONB_STRIP_NULLS(JSONB_BUILD_OBJECT( - 'min_fee_a', PP.min_fee_a, - 'min_fee_b', PP.min_fee_b, - 'max_block_size', PP.max_block_size, - 'max_tx_size', PP.max_tx_size, - 'max_bh_size', PP.max_bh_size, - 'key_deposit', PP.key_deposit, - 'pool_deposit', PP.pool_deposit, - 'max_epoch', PP.max_epoch, - 'optimal_pool_count', PP.optimal_pool_count, - 'influence', PP.influence, - 'monetary_expand_rate', PP.monetary_expand_rate, - 'treasury_growth_rate', PP.treasury_growth_rate, - 'decentralisation', PP.decentralisation, - 'entropy', PP.entropy, - 'protocol_major', PP.protocol_major, - 'protocol_minor', PP.protocol_minor, - 'min_utxo_value', PP.min_utxo_value, - 'min_pool_cost', PP.min_pool_cost, - 'cost_model', CM.costs, - 'price_mem', PP.price_mem, - 'price_step', PP.price_step, - 'max_tx_ex_mem', PP.max_tx_ex_mem, - 'max_tx_ex_steps', PP.max_tx_ex_steps, - 'max_block_ex_mem', PP.max_block_ex_mem, - 'max_block_ex_steps', PP.max_block_ex_steps, - 'max_val_size', PP.max_val_size, - 'collateral_percent', PP.collateral_percent, - 'max_collateral_inputs', PP.max_collateral_inputs, - 'coins_per_utxo_size', PP.coins_per_utxo_size + 'min_fee_a', pp.min_fee_a, + 'min_fee_b', pp.min_fee_b, + 'max_block_size', pp.max_block_size, + 'max_tx_size', pp.max_tx_size, + 'max_bh_size', pp.max_bh_size, + 'key_deposit', pp.key_deposit, + 'pool_deposit', pp.pool_deposit, + 'max_epoch', pp.max_epoch, + 'optimal_pool_count', pp.optimal_pool_count, + 'influence', pp.influence, + 'monetary_expand_rate', pp.monetary_expand_rate, + 'treasury_growth_rate', pp.treasury_growth_rate, + 'decentralisation', pp.decentralisation, + 'entropy', pp.entropy, + 'protocol_major', pp.protocol_major, + 'protocol_minor', pp.protocol_minor, + 'min_utxo_value', pp.min_utxo_value, + 'min_pool_cost', pp.min_pool_cost, + 'cost_model', cm.costs, + 'price_mem', pp.price_mem, + 'price_step', pp.price_step, + 'max_tx_ex_mem', pp.max_tx_ex_mem, + 'max_tx_ex_steps', pp.max_tx_ex_steps, + 'max_block_ex_mem', pp.max_block_ex_mem, + 'max_block_ex_steps', pp.max_block_ex_steps, + 'max_val_size', pp.max_val_size, + 'collateral_percent', pp.collateral_percent, + 'max_collateral_inputs', pp.max_collateral_inputs, + 'coins_per_utxo_size', pp.coins_per_utxo_size )) ) AS data - FROM - public.param_proposal PP - INNER JOIN cost_model CM ON CM.id = PP.cost_model_id + FROM + public.param_proposal AS pp + INNER JOIN cost_model AS cm ON cm.id = pp.cost_model_id WHERE - PP.registered_tx_id = ANY (_tx_id_list) + pp.registered_tx_id = ANY(_tx_id_list) -- UNION ALL -- SELECT - PR.announced_tx_id AS tx_id, + pr.announced_tx_id AS tx_id, JSONB_BUILD_OBJECT( - 'index', PR.cert_index, + 'index', pr.cert_index, 'type', 'pool_retire', 'info', JSONB_BUILD_OBJECT( - 'pool_id_bech32', PH.view, - 'pool_id_hex', ENCODE(PH.hash_raw, 'hex'), - 'retiring epoch', PR.retiring_epoch + 'pool_id_bech32', ph.view, + 'pool_id_hex', ENCODE(ph.hash_raw, 'hex'), + 'retiring epoch', pr.retiring_epoch ) ) AS data - FROM - public.pool_retire PR - INNER JOIN public.pool_hash PH ON PH.id = PR.hash_id + FROM + public.pool_retire AS pr + INNER JOIN public.pool_hash AS ph ON ph.id = pr.hash_id WHERE - PR.announced_tx_id = ANY (_tx_id_list) + pr.announced_tx_id = ANY(_tx_id_list) -- UNION ALL -- SELECT - PIC.tx_id, + pic.tx_id, JSONB_BUILD_OBJECT( - 'index', PU.cert_index, + 'index', pu.cert_index, 'type', 'pool_update', 'info', JSONB_BUILD_OBJECT( - 'pool_id_bech32', PIC.pool_id_bech32, - 'pool_id_hex', PIC.pool_id_hex, - 'active_epoch_no', PIC.active_epoch_no, - 'vrf_key_hash', PIC.vrf_key_hash, - 'margin', PIC.margin, - 'fixed_cost', PIC.fixed_cost::text, - 'pledge', PIC.pledge::text, - 'reward_addr', PIC.reward_addr, - 'owners', PIC.owners, - 'relays', PIC.relays, - 'meta_url', PIC.meta_url, - 'meta_hash', PIC.meta_hash + 'pool_id_bech32', pic.pool_id_bech32, + 'pool_id_hex', pic.pool_id_hex, + 'active_epoch_no', pic.active_epoch_no, + 'vrf_key_hash', pic.vrf_key_hash, + 'margin', pic.margin, + 'fixed_cost', pic.fixed_cost::text, + 'pledge', pic.pledge::text, + 'reward_addr', pic.reward_addr, + 'owners', pic.owners, + 'relays', pic.relays, + 'meta_url', pic.meta_url, + 'meta_hash', pic.meta_hash ) ) AS data - FROM - grest.pool_info_cache PIC - INNER JOIN public.pool_update PU ON PU.registered_tx_id = PIC.tx_id + FROM + grest.pool_info_cache AS pic + INNER JOIN public.pool_update AS pu ON pu.registered_tx_id = pic.tx_id WHERE - PIC.tx_id = ANY (_tx_id_list) + pic.tx_id = ANY(_tx_id_list) ) AS tmp - GROUP BY tx_id ), @@ -631,7 +621,7 @@ BEGIN FROM script WHERE - script.tx_id = ANY (_tx_id_list) + script.tx_id = ANY(_tx_id_list) AND script.type = 'timelock' ) AS tmp @@ -644,7 +634,7 @@ BEGIN tx_id, JSONB_AGG(data) AS list FROM ( - WITH + WITH all_redeemers AS ( SELECT redeemer.id, @@ -653,38 +643,39 @@ BEGIN redeemer.fee, redeemer.unit_steps, redeemer.unit_mem, - rd.hash as rd_hash, - rd.value as rd_value, - script.hash as script_hash, - script.bytes as script_bytes, - script.serialised_size as script_serialised_size, + rd.hash AS rd_hash, + rd.value AS rd_value, + script.hash AS script_hash, + script.bytes AS script_bytes, + script.serialised_size AS script_serialised_size, tx.valid_contract FROM redeemer INNER JOIN tx ON redeemer.tx_id = tx.id - INNER JOIN redeemer_data RD ON RD.id = redeemer.redeemer_data_id + INNER JOIN redeemer_data AS rd ON rd.id = redeemer.redeemer_data_id INNER JOIN script ON redeemer.script_hash = script.hash - WHERE redeemer.tx_id = ANY (_tx_id_list) + WHERE redeemer.tx_id = ANY(_tx_id_list) ), + spend_redeemers AS ( - SELECT - DISTINCT ON(redeemer.id) redeemer.id, - INUTXO.address, - IND.hash as ind_hash, - IND.value as ind_value + SELECT DISTINCT ON (redeemer.id) + redeemer.id, + inutxo.address, + ind.hash AS ind_hash, + ind.value AS ind_value FROM redeemer INNER JOIN tx_in ON tx_in.redeemer_id = redeemer.id - INNER JOIN tx_out INUTXO ON INUTXO.tx_id = tx_in.tx_out_id AND INUTXO.index = tx_in.tx_out_index - INNER JOIN datum IND ON IND.hash = INUTXO.data_hash - WHERE redeemer.tx_id = ANY (_tx_id_list) + INNER JOIN tx_out AS inutxo ON inutxo.tx_id = tx_in.tx_out_id AND inutxo.index = tx_in.tx_out_index + INNER JOIN datum AS ind ON ind.hash = inutxo.data_hash + WHERE redeemer.tx_id = ANY(_tx_id_list) ) + SELECT ar.tx_id, JSONB_BUILD_OBJECT( - 'address', - CASE + 'address', + CASE WHEN ar.purpose = 'spend' THEN - (SELECT address FROM spend_redeemers sr WHERE sr.id = ar.id) - ELSE NULL + (SELECT address FROM spend_redeemers AS sr WHERE sr.id = ar.id) END, 'script_hash', ENCODE(ar.script_hash, 'hex'), 'bytecode', ENCODE(ar.script_bytes, 'hex'), @@ -707,32 +698,33 @@ BEGIN SELECT JSONB_BUILD_OBJECT( 'hash', ENCODE(sr.ind_hash, 'hex'), 'value', sr.ind_value - ) FROM spend_redeemers sr WHERE sr.id = ar.id - ) ELSE NULL END + ) + FROM spend_redeemers AS sr WHERE sr.id = ar.id + ) END ) ) AS data FROM - all_redeemers ar + all_redeemers AS ar ) AS tmp GROUP BY tx_id ) SELECT - ENCODE(ATX.tx_hash, 'hex'), - ENCODE(ATX.block_hash, 'hex'), - ATX.block_height, - ATX.epoch_no, - ATX.epoch_slot, - ATX.absolute_slot, - EXTRACT(epoch from ATX.tx_timestamp)::integer, - ATX.tx_block_index, - ATX.tx_size, - ATX.total_output::text, - ATX.fee::text, - ATX.deposit::text, - ATX.invalid_before::text, - ATX.invalid_after::text, + ENCODE(atx.tx_hash, 'hex'), + ENCODE(atx.block_hash, 'hex'), + atx.block_height, + atx.epoch_no, + atx.epoch_slot, + atx.absolute_slot, + EXTRACT(EPOCH FROM atx.tx_timestamp)::integer, + atx.tx_block_index, + atx.tx_size, + atx.total_output::text, + atx.fee::text, + atx.deposit::text, + atx.invalid_before::text, + atx.invalid_after::text, COALESCE(( SELECT JSONB_AGG(tx_collateral_inputs) FROM ( @@ -743,7 +735,7 @@ BEGIN 'cred', payment_addr_cred ), 'stake_addr', stake_addr, - 'tx_hash', ACI.tx_hash, + 'tx_hash', aci.tx_hash, 'tx_index', tx_index, 'value', value, 'datum_hash', datum_hash, @@ -751,9 +743,9 @@ BEGIN 'reference_script', reference_script, 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) ) AS tx_collateral_inputs - FROM _all_collateral_inputs ACI - WHERE ACI.tx_id = ATX.id - GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ACI.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + FROM _all_collateral_inputs AS aci + WHERE aci.tx_id = atx.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, aci.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script ) AS tmp ), JSONB_BUILD_ARRAY()), ( @@ -764,7 +756,7 @@ BEGIN 'cred', payment_addr_cred ), 'stake_addr', stake_addr, - 'tx_hash', ACO.tx_hash, + 'tx_hash', aco.tx_hash, 'tx_index', tx_index, 'value', value, 'datum_hash', datum_hash, @@ -772,9 +764,9 @@ BEGIN 'reference_script', reference_script, 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) ) AS tx_collateral_outputs - FROM _all_collateral_outputs ACO - WHERE ACO.tx_id = ATX.id - GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ACO.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + FROM _all_collateral_outputs AS aco + WHERE aco.tx_id = atx.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, aco.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script LIMIT 1 -- there can only be one collateral output ), COALESCE(( @@ -787,7 +779,7 @@ BEGIN 'cred', payment_addr_cred ), 'stake_addr', stake_addr, - 'tx_hash', ARI.tx_hash, + 'tx_hash', ari.tx_hash, 'tx_index', tx_index, 'value', value, 'datum_hash', datum_hash, @@ -795,9 +787,9 @@ BEGIN 'reference_script', reference_script, 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) ) AS tx_reference_inputs - FROM _all_reference_inputs ARI - WHERE ARI.tx_id = ATX.id - GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ARI.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + FROM _all_reference_inputs AS ari + WHERE ari.tx_id = atx.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ari.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script ) AS tmp ), JSONB_BUILD_ARRAY()), COALESCE(( @@ -810,7 +802,7 @@ BEGIN 'cred', payment_addr_cred ), 'stake_addr', stake_addr, - 'tx_hash', AI.tx_hash, + 'tx_hash', ai.tx_hash, 'tx_index', tx_index, 'value', value, 'datum_hash', datum_hash, @@ -818,9 +810,9 @@ BEGIN 'reference_script', reference_script, 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) ) AS tx_inputs - FROM _all_inputs AI - WHERE AI.tx_id = ATX.id - GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AI.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + FROM _all_inputs AS ai + WHERE ai.tx_id = atx.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ai.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script ) AS tmp ), JSONB_BUILD_ARRAY()), COALESCE(( @@ -833,7 +825,7 @@ BEGIN 'cred', payment_addr_cred ), 'stake_addr', stake_addr, - 'tx_hash', AO.tx_hash, + 'tx_hash', ao.tx_hash, 'tx_index', tx_index, 'value', value, 'datum_hash', datum_hash, @@ -841,23 +833,22 @@ BEGIN 'reference_script', reference_script, 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) ) AS tx_outputs - FROM _all_outputs AO - WHERE AO.tx_id = ATX.id - GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AO.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script + FROM _all_outputs AS ao + WHERE ao.tx_id = atx.id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ao.tx_hash, tx_index, value, datum_hash, inline_datum, reference_script ) AS tmp ), JSONB_BUILD_ARRAY()), - COALESCE((SELECT AW.list FROM _all_withdrawals AW WHERE AW.tx_id = ATX.id), JSONB_BUILD_ARRAY()), - COALESCE((SELECT AMI.list FROM _all_mints AMI WHERE AMI.tx_id = ATX.id), JSONB_BUILD_ARRAY()), - COALESCE((SELECT AME.list FROM _all_metadata AME WHERE AME.tx_id = ATX.id), NULL), - COALESCE((SELECT AC.list FROM _all_certs AC WHERE AC.tx_id = ATX.id), JSONB_BUILD_ARRAY()), - COALESCE((SELECT ANS.list FROM _all_native_scripts ANS WHERE ANS.tx_id = ATX.id), JSONB_BUILD_ARRAY()), - COALESCE((SELECT APC.list FROM _all_plutus_contracts APC WHERE APC.tx_id = ATX.id), JSONB_BUILD_ARRAY()) - FROM - _all_tx ATX - WHERE ATX.tx_hash = ANY (_tx_hashes_bytea) -); + COALESCE((SELECT aw.list FROM _all_withdrawals AS aw WHERE aw.tx_id = atx.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT ami.list FROM _all_mints AS ami WHERE ami.tx_id = atx.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT ame.list FROM _all_metadata AS ame WHERE ame.tx_id = atx.id), NULL), + COALESCE((SELECT ac.list FROM _all_certs AS ac WHERE ac.tx_id = atx.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT ans.list FROM _all_native_scripts AS ans WHERE ans.tx_id = atx.id), JSONB_BUILD_ARRAY()), + COALESCE((SELECT apc.list FROM _all_plutus_contracts AS apc WHERE apc.tx_id = atx.id), JSONB_BUILD_ARRAY()) + FROM _all_tx AS atx + WHERE atx.tx_hash = ANY(_tx_hashes_bytea) + ); END; $$; -COMMENT ON FUNCTION grest.tx_info IS 'Get information about transactions.'; +COMMENT ON FUNCTION grest.tx_info IS 'Get information about transactions.'; -- noqa: LT01 diff --git a/files/grest/rpc/transactions/tx_metadata.sql b/files/grest/rpc/transactions/tx_metadata.sql index d4302614..dfc413d3 100644 --- a/files/grest/rpc/transactions/tx_metadata.sql +++ b/files/grest/rpc/transactions/tx_metadata.sql @@ -1,41 +1,41 @@ -CREATE FUNCTION grest.tx_metadata (_tx_hashes text[]) - RETURNS TABLE ( - tx_hash text, - metadata jsonb) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.tx_metadata(_tx_hashes text []) +RETURNS TABLE ( + tx_hash text, + metadata jsonb +) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY SELECT - T1.tx_hash, - METADATA_T.metadata + t1.tx_hash, + metadata_t.metadata FROM ( SELECT tx.id, - ENCODE(tx.hash, 'hex') as tx_hash + ENCODE(tx.hash, 'hex') AS tx_hash FROM public.tx WHERE - tx.hash::bytea = ANY ( + tx.hash::bytea = ANY( SELECT DECODE(hashes, 'hex') FROM UNNEST(_tx_hashes) AS hashes ) - ) T1 + ) AS t1 LEFT JOIN LATERAL ( SELECT JSONB_OBJECT_AGG( tx_metadata.key::text, tx_metadata.json - ) as metadata + ) AS metadata FROM tx_metadata WHERE - tx_id = T1.id - ) METADATA_T ON TRUE; + tx_id = t1.id + ) AS metadata_t ON TRUE; END; $$; -COMMENT ON FUNCTION grest.tx_metadata IS 'Get transaction metadata.'; - +COMMENT ON FUNCTION grest.tx_metadata IS 'Get transaction metadata.'; -- noqa: LT01 diff --git a/files/grest/rpc/transactions/tx_metalabels.sql b/files/grest/rpc/transactions/tx_metalabels.sql index 7b9b4c7f..c64f5f31 100644 --- a/files/grest/rpc/transactions/tx_metalabels.sql +++ b/files/grest/rpc/transactions/tx_metalabels.sql @@ -1,21 +1,26 @@ DROP FUNCTION IF EXISTS grest.tx_metalabels; - -CREATE FUNCTION grest.tx_metalabels() - RETURNS TABLE (key text) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.tx_metalabels() +RETURNS TABLE (key text) +LANGUAGE plpgsql +AS $$ BEGIN RETURN QUERY WITH RECURSIVE t AS ( - (SELECT tm.key FROM public.tx_metadata tm ORDER BY key LIMIT 1) + (SELECT tm.key + FROM public.tx_metadata tm + ORDER BY key LIMIT 1) UNION ALL - SELECT (SELECT tm.key FROM tx_metadata tm WHERE tm.key > t.key ORDER BY key LIMIT 1) + SELECT ( + SELECT tm.key + FROM tx_metadata tm + WHERE tm.key > t.key + ORDER BY key LIMIT 1) FROM t WHERE t.key IS NOT NULL ) + SELECT t.key::text FROM t WHERE t.key IS NOT NULL; END; $$; -COMMENT ON FUNCTION grest.tx_metalabels IS 'Get a list of all transaction metalabels'; - +COMMENT ON FUNCTION grest.tx_metalabels IS 'Get a list of all transaction metalabels'; -- noqa: LT01 diff --git a/files/grest/rpc/transactions/tx_status.sql b/files/grest/rpc/transactions/tx_status.sql index 617ebb18..64789fb5 100644 --- a/files/grest/rpc/transactions/tx_status.sql +++ b/files/grest/rpc/transactions/tx_status.sql @@ -1,26 +1,27 @@ -CREATE FUNCTION grest.tx_status (_tx_hashes text[]) - RETURNS TABLE ( - tx_hash text, - num_confirmations integer) - LANGUAGE plpgsql - AS $$ +CREATE OR REPLACE FUNCTION grest.tx_status(_tx_hashes text []) +RETURNS TABLE ( + tx_hash text, + num_confirmations integer +) +LANGUAGE plpgsql +AS $$ DECLARE _curr_block_no word31type; BEGIN + SELECT + max(block_no) INTO _curr_block_no + FROM + block b; + RETURN QUERY ( SELECT - max(block_no) INTO _curr_block_no - FROM - block b; - - RETURN QUERY ( - select HASHES, (_curr_block_no - b.block_no) - from UNNEST(_tx_hashes) WITH ORDINALITY HASHES - left outer join tx t on t.hash = DECODE(HASHES, 'hex') - left outer join block b on t.block_id = b.id - ORDER BY ordinality - ); + HASHES, + (_curr_block_no - b.block_no) + FROM UNNEST(_tx_hashes) WITH ORDINALITY HASHES + LEFT OUTER JOIN tx AS t ON t.hash = DECODE(HASHES, 'hex') + LEFT OUTER JOIN block AS b ON t.block_id = b.id + ORDER BY ordinality + ); END; $$; -COMMENT ON FUNCTION grest.tx_status IS 'Returns number of blocks that were created since the block containing a transactions with a given hash'; - +COMMENT ON FUNCTION grest.tx_status IS 'Returns number of blocks that were created since the block containing a transactions with a given hash'; -- noqa: LT01 diff --git a/files/grest/rpc/transactions/tx_utxos.sql b/files/grest/rpc/transactions/tx_utxos.sql index b8a56438..af477a70 100644 --- a/files/grest/rpc/transactions/tx_utxos.sql +++ b/files/grest/rpc/transactions/tx_utxos.sql @@ -1,11 +1,11 @@ -CREATE OR REPLACE FUNCTION grest.tx_utxos (_tx_hashes text[]) - RETURNS TABLE ( - tx_hash text, - inputs jsonb, - outputs jsonb - ) - LANGUAGE PLPGSQL - AS $$ +CREATE OR REPLACE FUNCTION grest.tx_utxos(_tx_hashes text []) +RETURNS TABLE ( + tx_hash text, + inputs jsonb, + outputs jsonb +) +LANGUAGE plpgsql +AS $$ DECLARE _tx_hashes_bytea bytea[]; _tx_id_list bigint[]; @@ -13,25 +13,20 @@ BEGIN -- convert input _tx_hashes array into bytea array SELECT INTO _tx_hashes_bytea ARRAY_AGG(hashes_bytea) FROM ( - SELECT - DECODE(hashes_hex, 'hex') AS hashes_bytea - FROM - UNNEST(_tx_hashes) AS hashes_hex + SELECT DECODE(hashes_hex, 'hex') AS hashes_bytea + FROM UNNEST(_tx_hashes) AS hashes_hex ) AS tmp; -- all tx ids SELECT INTO _tx_id_list ARRAY_AGG(id) FROM ( - SELECT - id - FROM - tx - WHERE tx.hash = ANY (_tx_hashes_bytea) + SELECT id + FROM tx + WHERE tx.hash = ANY(_tx_hashes_bytea) ) AS tmp; RETURN QUERY ( WITH - -- tx id / hash mapping _all_tx AS ( SELECT @@ -39,7 +34,7 @@ BEGIN tx.hash AS tx_hash FROM tx - WHERE tx.id = ANY (_tx_id_list) + WHERE tx.id = ANY(_tx_id_list) ), _all_inputs AS ( @@ -47,18 +42,18 @@ BEGIN tx_in.tx_in_id AS tx_id, tx_out.address AS payment_addr_bech32, ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, - SA.view AS stake_addr, + sa.view AS stake_addr, ENCODE(tx.hash, 'hex') AS tx_hash, tx_out.index AS tx_index, tx_out.value::text AS value, - ( CASE WHEN MA.policy IS NULL THEN NULL + ( CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', aic.decimals, - 'quantity', MTO.quantity::text + 'quantity', mto.quantity::text ) END ) AS asset_list @@ -66,13 +61,13 @@ BEGIN tx_in INNER JOIN tx_out ON tx_out.tx_id = tx_in.tx_out_id AND tx_out.index = tx_in.tx_out_index - INNER JOIN tx on tx_out.tx_id = tx.id - LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id - LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id - LEFT JOIN multi_asset MA ON MA.id = MTO.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id WHERE - tx_in.tx_in_id = ANY (_tx_id_list) + tx_in.tx_in_id = ANY(_tx_id_list) ), _all_outputs AS ( @@ -80,34 +75,34 @@ BEGIN tx_out.tx_id, tx_out.address AS payment_addr_bech32, ENCODE(tx_out.payment_cred, 'hex') AS payment_addr_cred, - SA.view AS stake_addr, + sa.view AS stake_addr, ENCODE(tx.hash, 'hex') AS tx_hash, tx_out.index AS tx_index, tx_out.value::text AS value, - ( CASE WHEN MA.policy IS NULL THEN NULL + ( CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(MA.policy, 'hex'), - 'asset_name', ENCODE(MA.name, 'hex'), - 'fingerprint', MA.fingerprint, + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, 'decimals', aic.decimals, - 'quantity', MTO.quantity::text + 'quantity', mto.quantity::text ) END ) AS asset_list FROM tx_out INNER JOIN tx ON tx_out.tx_id = tx.id - LEFT JOIN stake_address SA ON tx_out.stake_address_id = SA.id - LEFT JOIN ma_tx_out MTO ON MTO.tx_out_id = tx_out.id - LEFT JOIN multi_asset MA ON MA.id = MTO.ident - LEFT JOIN grest.asset_info_cache aic ON aic.asset_id = MA.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id WHERE - tx_out.tx_id = ANY (_tx_id_list) + tx_out.tx_id = ANY(_tx_id_list) ) SELECT - ENCODE(ATX.tx_hash, 'hex'), + ENCODE(atx.tx_hash, 'hex'), COALESCE(( SELECT JSONB_AGG(tx_inputs) FROM ( @@ -118,14 +113,14 @@ BEGIN 'cred', payment_addr_cred ), 'stake_addr', stake_addr, - 'tx_hash', AI.tx_hash, + 'tx_hash', ai.tx_hash, 'tx_index', tx_index, 'value', value, 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) ) AS tx_inputs - FROM _all_inputs AI - WHERE AI.tx_id = ATX.tx_id - GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AI.tx_hash, tx_index, value + FROM _all_inputs AS ai + WHERE ai.tx_id = atx.tx_id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ai.tx_hash, tx_index, value ) AS tmp ), JSONB_BUILD_ARRAY()), COALESCE(( @@ -138,23 +133,22 @@ BEGIN 'cred', payment_addr_cred ), 'stake_addr', stake_addr, - 'tx_hash', AO.tx_hash, + 'tx_hash', ao.tx_hash, 'tx_index', tx_index, 'value', value, 'asset_list', COALESCE(JSONB_AGG(asset_list) FILTER (WHERE asset_list IS NOT NULL), JSONB_BUILD_ARRAY()) ) AS tx_outputs - FROM _all_outputs AO - WHERE AO.tx_id = ATX.tx_id - GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, AO.tx_hash, tx_index, value + FROM _all_outputs AS ao + WHERE ao.tx_id = atx.tx_id + GROUP BY payment_addr_bech32, payment_addr_cred, stake_addr, ao.tx_hash, tx_index, value ) AS tmp ), JSONB_BUILD_ARRAY()) FROM - _all_tx ATX - WHERE ATX.tx_hash = ANY (_tx_hashes_bytea) + _all_tx AS atx + WHERE atx.tx_hash = ANY(_tx_hashes_bytea) ); END; $$; -COMMENT ON FUNCTION grest.tx_utxos IS 'Get UTXO set (inputs/outputs) of transactions.'; - +COMMENT ON FUNCTION grest.tx_utxos IS 'Get UTXO set (inputs/outputs) of transactions.'; -- noqa: LT01 diff --git a/files/grest/rpc/views/account_list.sql b/files/grest/rpc/views/account_list.sql index 2656a359..2d26218b 100644 --- a/files/grest/rpc/views/account_list.sql +++ b/files/grest/rpc/views/account_list.sql @@ -1,10 +1,8 @@ DROP VIEW IF EXISTS grest.account_list; CREATE VIEW grest.account_list AS - SELECT - STAKE_ADDRESS.VIEW AS ID - FROM - STAKE_ADDRESS; +SELECT stake_address.view AS id +FROM + stake_address; COMMENT ON VIEW grest.account_list IS 'Get a list of all accounts'; - diff --git a/files/grest/rpc/views/asset_list.sql b/files/grest/rpc/views/asset_list.sql index b41ecaa7..7fc71ded 100644 --- a/files/grest/rpc/views/asset_list.sql +++ b/files/grest/rpc/views/asset_list.sql @@ -1,12 +1,12 @@ DROP VIEW IF EXISTS grest.asset_list; CREATE VIEW grest.asset_list AS - SELECT - ENCODE(MA.policy, 'hex') AS policy_id, - ENCODE(MA.name, 'hex') AS asset_name, - MA.fingerprint - FROM - public.multi_asset MA - ORDER BY MA.policy, MA.name; +SELECT + ENCODE(ma.policy, 'hex') AS policy_id, + ENCODE(ma.name, 'hex') AS asset_name, + ma.fingerprint +FROM + public.multi_asset AS ma +ORDER BY ma.policy, ma.name; COMMENT ON VIEW grest.asset_list IS 'Get the list of all native assets'; diff --git a/files/grest/rpc/views/asset_token_registry.sql b/files/grest/rpc/views/asset_token_registry.sql index c376deed..ca5c7561 100644 --- a/files/grest/rpc/views/asset_token_registry.sql +++ b/files/grest/rpc/views/asset_token_registry.sql @@ -1,17 +1,16 @@ DROP VIEW IF EXISTS grest.asset_token_registry; CREATE VIEW grest.asset_token_registry AS - SELECT - asset_policy AS policy_id, - asset_name, - name AS asset_name_ascii, - ticker, - description, - url, - decimals, - logo - FROM - grest.asset_registry_cache -; +SELECT + asset_policy AS policy_id, + asset_name, + name AS asset_name_ascii, + ticker, + description, + url, + decimals, + logo +FROM + grest.asset_registry_cache; COMMENT ON VIEW grest.asset_token_registry IS 'Get a list of assets registered via token registry on github'; diff --git a/files/grest/rpc/views/blocks.sql b/files/grest/rpc/views/blocks.sql index b0387e2b..dc50da9f 100644 --- a/files/grest/rpc/views/blocks.sql +++ b/files/grest/rpc/views/blocks.sql @@ -1,25 +1,25 @@ DROP VIEW IF EXISTS grest.blocks; CREATE VIEW grest.blocks AS - SELECT - ENCODE(B.HASH::bytea, 'hex') AS HASH, - b.EPOCH_NO AS EPOCH_NO, - b.SLOT_NO AS ABS_SLOT, - b.EPOCH_SLOT_NO AS EPOCH_SLOT, - b.BLOCK_NO AS BLOCK_HEIGHT, - b.SIZE AS BLOCK_SIZE, - EXTRACT(epoch from b.TIME)::integer AS BLOCK_TIME, - b.TX_COUNT, - b.VRF_KEY, - ph.VIEW AS POOL, - b.PROTO_MAJOR, - b.PROTO_MINOR, - b.OP_CERT_COUNTER - FROM - BLOCK B - LEFT JOIN SLOT_LEADER SL ON SL.ID = B.SLOT_LEADER_ID - LEFT JOIN POOL_HASH PH ON PH.ID = SL.POOL_HASH_ID - ORDER BY - B.ID DESC; +SELECT + ENCODE(b.hash::bytea, 'hex') AS hash, + b.epoch_no AS epoch_no, + b.slot_no AS abs_slot, + b.epoch_slot_no AS epoch_slot, + b.block_no AS block_height, + b.size AS block_size, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + b.tx_count, + b.vrf_key, + ph.view AS pool, + b.proto_major, + b.proto_minor, + b.op_cert_counter +FROM + block AS b +LEFT JOIN slot_leader AS sl ON sl.id = b.slot_leader_id +LEFT JOIN pool_hash AS ph ON ph.id = sl.pool_hash_id +ORDER BY + b.id DESC; COMMENT ON VIEW grest.blocks IS 'Get detailed information about all blocks (paginated - latest first)'; diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 0b3b4eb8..2027146d 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -137,26 +137,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) - - # FAQ - - ### Is there a price attached to using services? - For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). - - ### Who are the folks behind Koios? - It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) - who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many - for experiments. - - ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. - - ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). - - ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/community.html) x-logo: url: "https://api.koios.rest/images/koios.png" diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index b665fc58..f9d5c3e0 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -137,26 +137,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) - - # FAQ - - ### Is there a price attached to using services? - For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). - - ### Who are the folks behind Koios? - It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) - who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many - for experiments. - - ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. - - ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). - - ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/community.html) x-logo: url: "https://api.koios.rest/images/koios.png" diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 3439d4f2..6d956f7c 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -137,26 +137,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) - - # FAQ - - ### Is there a price attached to using services? - For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). - - ### Who are the folks behind Koios? - It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) - who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many - for experiments. - - ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. - - ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). - - ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/community.html) x-logo: url: "https://api.koios.rest/images/koios.png" diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 1c6ad088..debc78c1 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -137,26 +137,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) - - # FAQ - - ### Is there a price attached to using services? - For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). - - ### Who are the folks behind Koios? - It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) - who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many - for experiments. - - ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. - - ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). - - ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/community.html) x-logo: url: "https://api.koios.rest/images/koios.png" diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 43fd9438..656eaf17 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -136,26 +136,7 @@ info: # Community projects - A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/api-calls-tools-and-libraries) - - # FAQ - - ### Is there a price attached to using services? - For most of the queries, there are no charges. But there are DDoS protection and strict timeout rules (see API Usage) that may prevent heavy consumers from using this *remotely* (for which, there should be an interaction to ensure the usage is proportional to sizing and traffic expected). - - ### Who are the folks behind Koios? - It will be increasing list of community builders. But for initial think-tank and efforts, the work done is primarily by [guild-operators](https://cardano-community.github.io/guild-operators) - who are a well-recognised team of members behind Cardano tools like CNTools, gLiveView, topologyUpdater, etc. We also run a parallel a short (60-min) epoch blockchain, viz, guild used by many - for experiments. - - ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. - - ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). - - ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. + A big thank you to the following projects who are already starting to use Koios from early days. A list of tools, libraries and projects utilising Koios (atleast those who'd like to be named) can be found [here](https://www.koios.rest/community.html) x-logo: url: "https://api.koios.rest/images/koios.png" From 26db0df4d751f0d3ce405829cb5541eefd4d2c84 Mon Sep 17 00:00:00 2001 From: "Cardano Blockhouse [CBH]" <90690011+cardano-blockhouse@users.noreply.github.com> Date: Tue, 15 Aug 2023 09:20:54 +0200 Subject: [PATCH 79/86] Laravel PHP client added (#228) ## Description Added my laravel package to the clients list ## Where should the reviewer start? Lines 44 to 47 ## Motivation and context There has been no PHP library for the Koios APi yet ## How has this been tested? All endpoints have been tested by me by calling them from a controller. --- projects.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/projects.json b/projects.json index 9fddf57f..7694ca7b 100644 --- a/projects.json +++ b/projects.json @@ -41,6 +41,10 @@ { "text": "TypeScript Client (Tiny)", "link": "https://github.com/ray-network/koios-tiny-client" + }, + { + "text": "Laravel PHP Client", + "link": "https://github.com/cardano-blockhouse/cardano-koios-api" } ], "Community Projects/Tools": [ From 86bbdef943c8532c861030c954210d755be7f817 Mon Sep 17 00:00:00 2001 From: Marco Date: Thu, 24 Aug 2023 03:41:24 +0200 Subject: [PATCH 80/86] feat: add Tabulali project (#229) ## Description Add Tabulali to projects. Tabulali is a simple wallet aggregator that combines the data of multiple wallets without the need to enter your seed phrase. The main use case is to keep track of your portfolio in a convenient way. It can be used as a website or even installed as a progressive web app on your phone. ## Where should the reviewer start? `projects.json` ## Motivation and context Add a project that uses Koios. ## Which issue it fixes? n/a ## How has this been tested? can't be tested --- images/projects/tabulali-512.png | Bin 0 -> 49146 bytes projects.json | 8 ++++++++ 2 files changed, 8 insertions(+) create mode 100644 images/projects/tabulali-512.png diff --git a/images/projects/tabulali-512.png b/images/projects/tabulali-512.png new file mode 100644 index 0000000000000000000000000000000000000000..91b4621283c85eac19c7a8b3c82a0c817e7400ff GIT binary patch literal 49146 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 z⁣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 literal 0 HcmV?d00001 diff --git a/projects.json b/projects.json index 7694ca7b..ca105c5e 100644 --- a/projects.json +++ b/projects.json @@ -160,6 +160,14 @@ "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/raywallet.png" } }, + { + "text": "Tabulali", + "link": "https://eddex.github.io/tabulali/", + "logo": { + "dark": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/tabulali-512.png", + "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/tabulali-512.png" + } + }, { "text": "TosiDrop", "link": "https://tosidrop.io", From 94182f1de0e6062fc4cda105e9b8dcfc39821158 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Aug 2023 14:13:54 +0100 Subject: [PATCH 81/86] CF logo adj (#231) Co-authored-by: stakelovelace --- images/projects/cf_dark.svg | 904 ++++++++++++++++++++++++++++++----- images/projects/cf_light.svg | 204 ++++---- 2 files changed, 899 insertions(+), 209 deletions(-) diff --git a/images/projects/cf_dark.svg b/images/projects/cf_dark.svg index 6144319e..764324f0 100644 --- a/images/projects/cf_dark.svg +++ b/images/projects/cf_dark.svg @@ -1,112 +1,802 @@ - - + + + + + + + + + +]> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + RqQSKGMkk1PochEHczJTGrhQUZApTC7iYD4cLgoOF20qyBQEHC4KPUIcTC0B1oUmEKZL6MgDc84S +Jlzi1wkwSIIIbiQKfpAhQVYJEHkZmAMKfpAwS/DRoG6IGD92JAcUAc5qBI4Jp1GANkSM57Sxs+Bw +OegQIB7JAQIHKGO0AI/kAGShwHQIY0dy0DmIl5OMmkqb3zygJjP1C52IGTGdg4gZYSMQpgtxByNI +FTYzjA9phDTOgwlpgDJGm2FGzwOE6UIWitLmwxC7MB2neVCRVYhBq8jMBMoIbBhEPrsWDCL/SBlE +fvOAmmGXp/Kg1ScywnSgMMIg8pwmwy5M10k2lYRcsfmOTxl04IbnCwEoD5ipiqfyQXWIAlDgpzAd +p1lIDqu7ikMDKGMqJBdWxwUI03kgcvS17Fx4fMoqFJ0SKtSQySnUnBZhQn0NEbi48HDaFCkQDExP +xkTBIznoPBYeUfHK2HDhcYgCaIJEDI97MmScC0+sQoERU14oMoBysNQRYTWnSYAwMRKIYKGyACFx +MEsoThMp+MEDp81EGopXLgItT+Q0B8rMFGoOcgr1KCI0U5iu0zCRMmh5XE1oTuuAjDCdw4IDh2Lh +cB4MpnNY6LxJol4H+Upqu2abPd9622Wd2+y/l06/ddp677e1uG3+Kufc7K3UWsc3Xyrxrd0zP530 +Sjo3W2vGltZac5X0ep539p05Y3nn9vQnvhjX6z3f/qXUfdba0umGP9uL/22eVLrnO2fNM0tKN/p/ +vU5KZ7Xyp+Nc8U/a9XqVlm5fcf5Z78yNZZ2VU91ie/2/s6US521WTO3/xfVav7nWx5Lmrf7NnjH2 +WbP0mh3bf2xr95Wdt5prtXg+bb/S66041+xfqbR4m5a6Y1zttVR69W1i+27zxJhelxNv89bO2Ga3 +j+WlrqlqppY+vre+Y9yYNm1r5cTbzRQ3/UrzT5mnb9n75r/WYu8pL97q21lvnf61pZ2uqbac5+2b +8fyuVXrear23cXem+aWdF2O3uFp7Z5V2+lY/v50/P9fbMuft3vv43vd8XTa92es7futzYonzVh3P +7+56n0pKKYe6pfe+Y5uvT0zrxNTrZ+nTt3o/u9fZjm2+//hWbLNLn66JvPuvxbX6lZT6dt9rzthm +PP0vxu24XvxWXnqtN27HTfP9ivFTfKnE1DdrJ63zYmtxyzwtbWyvTzurtNQ3/5/04opzztTppX+z +01xv//SePT1LSjtbmivGVTb1Dee+b6+s1DXV5v0959N6rbzTt2vt7Jtx7cdyUt9s9enZ67+9kk7f +7Jw+K74UXyyd+mbnZ+uO63uWc/qGn1I6/eK/WN5ZbXU6P1tasazTt3lrrTlnm2uWdFrOrT0dO6ZY +fvUNP85/+3HTp117Wr+X9sxfG0/s2XaV+dr6XWnba1vi6hvOjvtvY2qrvNe3p/m224m9Zum4zrbZ +/tubM3U766S2Smt9+2rbTos/35Y3/7z4UkttV4l91r6467RZNvYNe3eujSum9N6cafu8l2JJa8Y0 +Z89ZYuub53mxz/ptqczYNdV2a3++9t72/5vrpHZ+09u53lppnf/S7az1UkqnnNm3mSmedH67W2mr +b9j+7a8YX4yl52nd2n/P2FpJq2+17Vv81s5uibFqw9/Xb32Mu6vj+rNOm6/M1jeaffZ0jPPN0rFv +1emt1hu7vdLO74y/Sop9+/m1cbe1T2W1vllbbc3S9qw55+m5JbW+3e57bc3tOedKHc+meGKX1brG +2WjP69N6pv2z9lvrmLpbea1vleKcr783rfKzbz8rrT67tnXZ17c6qd/p2F5KpWd/ijt30+5vmbFv +t9Y868W4qaTXt7cT54q9/VJpsW9f76Vvb1N6ZX17c7aW2knlxL5NOrPjOq1XK+m8dVpq/18+9i3T +i2elOc9/+bRybM87fdab8ctu37Djb1tvbWzlvL5hSunNNuf/2jdXfPO9VDZ2jbNhXDOt3vOvtO1b +9pnpvHjDbm/G//NpY5k11WZxvjXX206xu1M7b762yseb/6X53WJsX9L+b4tx49tdp7zZNdG0NM9Z +s88rMXaNs93qt2abs2dqZb0b9tvVPv7HWF7s27v9SWv/pdOlvZut19Z8q1P7Ml/f3GlXPCm116mc +eHvvSy39mvvKv67BNvrd+Xa2t86Wn7e/X+ultWt2Oa81gSRILE3E2fC91/3dLc5X1rxVa+fENdeL +W2L37TG+s2mu8zOVM28T4+t/cXt12T9npt4Ye75Vdt5szrX79s3fst+36tji3LbpxVdevFmLvWan +2eIrs/t2e/bMbjOef2XF2/U6vVZsG1N53TXO5nPaam3FXymWFW/4r1drM8bYSuq+5do047a0MW5p +8Xbda3u734ylu2/13XH125PmKTHeaO3c1O2cWOZ2jbNZx13nxLktdul5s22vW5szrVnm9g137fea +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 +eau0eKMX03csf87NcbW1Gz/OF0uMt1q7b703Y3plz6lxtr9vH3t269dKzxuu9mlfO3tu0+aa62yn +01JJ8zaptfS+T3fpc056vbP9Km3entL7+c7sVM45Ndh272ef1b/WptLmLePplfZ195d2zi3PWnNT +WzPNVlq8eW37Gef5dkrq063X7jtz/5XtU1PRd2ovtZji+vOr9dlZTrxZ3PPrZ6f/8ntul146q9+3 +lFZ58ea34kwbY0uzxPk1Fc0z5/z5Ss8bpfMzdtu11mn9WndLpzf9SWXOv81qu62d2GKf9qfF87ac +PTUVpY271vk/H3em9+L81jPuvp5d1nxv9nwphm3Zc+1q8c9Js+y80ceZzly9tpXUJ811dq6fqbvM ++Ldbfb5Tl7luM+NJq6UX/5SdXxP5bTxnrvgzlrRu83O2s9o5++a+mFJqZdcN4+l+H3ulV1Jc6a2z +27p1mS+9ldbtnVp3/Bv9jL/nU29Z60bvrBfXiqm1sq9Pat/xzVW23fDtOr/O9kpltv4UY4rdUiyp +3S7td2ofP316a6W5ZbZb51zp7K612nvvxJt/dexWNnbHFeNqcbVe5bVbrvWtnbVaiyXFdXtaq9On +0toNV5uvzXTifDmPreO+90pct+rztve7rXVa25M2/UsztfarxPU1kaW4zomvtbLSzat1+xY7rld2 +/c1S76bv0ulmM53uLif9nOnNf7Ol9so5X+P0rLetxVV+3b7Ox/Yd2zlftsU+n2ZqczeuMtvN4m7r +9WqqrdKM8cTYJ7VUzrs5fZwxpp/zlXb+xO60YjrlvdP+T0uppNT6tNjtZ5f0bjPPt/jO+/nKWbFn +OjOmtFZapbVbrX3722Z7rbzV2r/zYjuzf8/ZXr3ezrgtzlTi+RpnUvzf2K+c9Lfq3/Urpbd+vTS/ +06Z+q523WisznffzT9v2Zlnrz2sv9b+zrdPGNFvvae+0duZqqXu+PfPNk947rXVKPVNJPecsbf3N +5krz3/Y7Z+63NlNrm1r7srZvld5+POXTzzhn3LO7sa1uJ77v/7LO/zw/50lvri3xf32b5/wp6V86 +J32bs6wWY5xnpphKvzbbSr3nlT1f47zNP9t2vthlphu29NLp9Nor+9KZs50Wd0uv29Pb7z5r9sfV +ZntzprLWDc+acc5vKaW4P3fOMtcNU2v93r6frWzr2+N869/pbrfPfa/Fk07XVHPWameuV167zc5O +u+m91+m8sm9nKv36RuecjqfFmNKX2G61McY241uztPe/Wr+z3lmvnBbf77cvc90svdTe3LRvpTRj +anPTbixn3b4nbYyvzfTKTGtuim91mqn0unmmFdeX9fqW7+PH1t37v///5jspttdntp0xzZTKTH3L +s947b/Wn2Kvbp9mnN85PrWc672OanX6Vufp2sb3ZXtu3qbT+W31vm+3si//9sb1Xdp12Vnuf0rbY +zs+P6ax3WuwV267emFKLO9evXrHs+Vt93G7pU6eN7aRuJZ2vqd567lrppfa6vW39ztu0b5304pn9 +UozfJZ2/+XTc1fZP/DXTSxtjmf812EY7+8zWZjuxpNRmn/TxlfRf42R7p6wY1+863/5tjD2/xHjm +zzN7rnnOTLHtnzM7fnwz7ksnlth/ezpxrRj/rFTW7But/U2zpfe2zP7brJm219uVvsS527/fXf6/ +xtn+cddun984X/f77T+trP9bpn3fZlpzvd64v7rM/xvuab9xzvSvpHdOa/OkWfp8TfX4p+fa7329 +J25LJ6bY5q4Z00oprlT6/C1bt41pz4yvvRjjmfF8KvF/dZ+O63RZsW/TXjw7S/oaZ9rOT7HLOWs/ +tT9dNv6N1muvzbVOO/GcmU5Mc5UV/3u91+lfLO37lnO7d5YX/4adWtwVv+Mp/33L77dvnbdfdv7t +P3f2LK37Rilud/vW4ikxnna6T/xuZ1tKZ6Y1Yzzxz3yr7SnfffN8c/2nkzaV+f42McbfPu97rRTf +mamm2pzaWfNTit1WS/vWm3GdVjrFbuutuf3St79VfHOm2F6/VeL37fP/tXd2den3Ndj2mWJb8ze+ +WPbFM3d1amW9XzXO5rba9nnt/VtppfbWS2eW7rOv19qZ2pnvtV67tsQ9NdVGZ8VeJ6aW2myfvqX1 +L5W25/bWfc7q9BtLp7ZvvZVmLGnPjT6u195pLbayTkunzRO3S9uTvj9uS+3b7PTO2XP7SjGm2dan +eHbPTPH9firf52arY4oxtXfi7kp7SutTU233a519Hf+lc9I7/VKfW/7/irH16e2enVKf252PL35r +f1J5Z9/3t5a+xD7vvNkb284tq8+N+rV/a3vNVc73DeNL/U6L8ZyPm96m3o/l1+qzOp13ys+TTv+c +H3vOnTOWOft2/76tP7/znbRxnrjl92O//ta9crZ68WP8dD5+jDvn/PbzzNL2bz//zqYVW4xzxdli +LN9/cztxxT27dk/H1eJ877/E3k1xrhVfmf9xxrL/t8fVet/2/9u5821qO2fp/1vGuc5bLaX5Zc2z +VkofW4vlf/XbXTtbTF9Sd/dab67yf2ba+dqLr5XVX1PNm9t/Utu25e17p//353eX3r9Nv54vbov/ +zuy13va/V9bsGmyz9zN9p41pprZeimtfb5czu8bZML54TlsxrvP67PoXZ/wt/f7296nbKnPebPfN +M1vcdOJ5/TO9Nd/bj/PbiWutmd5Z5/vPjOfMn93256YW50tnpW/xlX1fE+H8M093x9blO/aJrWdc ++15Z73bvzNQ2pdjnnRZX73a39aW9W86WZmt7dl/59bc6qb0Vz9l5VlupfWwrdWnpa6ot07ZeK65+ +q1f/mens92mlpb9hWyv9ib/e6/Y2nfSvdZeX/jZ91lqt5/xT3vbNaX3smNqcp6T1NRilFk9KsbR9 +pz++11tOTnV678Q1T5sxvThPWe9v+PZTevv9eqa0r8UX377ZKaYv7f3tafafNldaW37ecj+1tqtb +K/8rxU5zWyx7Wntp7qfzZ/06J6Z33ndLK6XzUlnxb7dtUzqbfq7+1PGtdfZnv/OpW/ySzuuX3jvf +Sop/4p/VzuzXupz4t0p74jzzzPnWr+/esvGl91pba358afZ7u7v2lfdWCv9Jv//Wx5az0c7W4mnn +bHtrzvStS1x/y42fWnpxrfKtb15znXlSzy5vfU2kcbabbW57/bt/zoyxzX1vpjLX3yyuPWu+1imu +X7PNXaf9zf3Sm+d9x7kd16b0c7XS2tc422yKr8U354xvrn27p+Ob550Y34xxzvhaf5rlvL95xbTr +zJROnGmuuf++xnmfl86+8uJs8b0127by728+r+1377dt87Rdrc/72+3vnLvmzi1n9W06vk7n00qp +rPY12Dbd/+Ypa/VtfraZus2zsXT7lnPd77yP56VWXp8UYzy94lvbc1ebe76c75ulTelj2Tb3z3nn +zJfK+77h99yf/zp9Oa1v0+etue/9K322xbO/ztn26fTvWZ1m+dM37DNfOnNft9Kva6oNV2pn53l9 +s3X232pptThjmiu+NN865X23+U483e9be+md9F7p71u23hZXe39Kn1+/frf9Kq/702txnVXi7BR/ +T3vvy+k240ltbjxty3fcj7ttrZhmOj339Pcsq62cmye1jivF1l9iuuVLn/rtWW2WeNbZt07rGEtL +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 + + diff --git a/images/projects/cf_light.svg b/images/projects/cf_light.svg index e924fed5..2eede0fe 100644 --- a/images/projects/cf_light.svg +++ b/images/projects/cf_light.svg @@ -1,112 +1,112 @@ - + + y="0px" viewBox="0 0 411 394" style="enable-background:new 0 0 411 394;" xml:space="preserve"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - + - - - + + + - - + v6.6c-1.9,0.8-4,1.2-6,1.2c-4.2,0-7.1-2.2-8-5.7h-0.6c-2.8,3.5-6.9,5.8-13.5,5.8C222.2,299.9,213.6,298.3,213.6,288z M240.2,282 + v-1.9c-9.2,0-16.7,1.3-16.7,6.9c0,3.4,2.2,5.2,6.4,5.2C236.4,292.2,240.2,288.1,240.2,282L240.2,282z"/> + + From d51289f53e2c035ecc13127099b3a878637b081e Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 22 Sep 2023 23:28:18 +0100 Subject: [PATCH 82/86] Add files via upload (#235) koios svg logo ## Description ## Where should the reviewer start? ## Motivation and context ## Which issue it fixes? ## How has this been tested? --- images/Koios.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 images/Koios.svg diff --git a/images/Koios.svg b/images/Koios.svg new file mode 100644 index 00000000..459182d7 --- /dev/null +++ b/images/Koios.svg @@ -0,0 +1 @@ + \ No newline at end of file From 9b3460b049f4cb3804316b95694af1cfed2a2985 Mon Sep 17 00:00:00 2001 From: RdLrT <3169068+rdlrt@users.noreply.github.com> Date: Sat, 14 Oct 2023 00:30:08 +1100 Subject: [PATCH 83/86] Koios v1.1.0rc (#238) ## Description [Re-created from #227 post renaming base-branch] - [x] pool_list: Return all fields from pool_info_cache (latest pool entry) - [x] Remove filter for returning only 'registered' pools from pool_list, pool_relays and pool_metadata endpoints - [x] Few more linting polishing (remove trailing spaces, move single column/table references onto same line) - [x] Update pool_info to return same metadata as pool_list and pool_metadata (latest w/o fallback) - [x] Update all tx_in joins to use the new tx_out.consumed_by_tx_in_id - [x] Dont include epoch_params in epoch_info_cache, update references accordingly - [x] Update `*_txs` as per #186 (point 4) - [x] Update `*_utxos` as per #186 (point 3) - [x] credential_utxos - [x] address_utxos - [x] account_utxos - [x] utxo_info - [x] asset_utxos - [x] script_utxos - [x] Add script_info and align all endpoints for scripts - [x] Add temporary cron for fixing epoch count mismatch - [x] Add pool_registrations/pool_deregistrations endpoint - [x] Add retired txs to `pool_updates` (other fields for such transactions will return `null`) - [x] Add reward_withdrawals/treasury_withdrawals endpoint - [x] Simplify address_assets and account_assets heirarchy to return flat table (helps do horizontal filtering as desired) - [x] API Spec updates: - [x] Add section for Authentication - [x] Bump Koios version - [x] pool_list - [x] pool_metadata - [x] pool_relay - [x] `*_txs` - [x] `*_utxos` - [x] `address_assets` - [x] `account_assets` - [x] `epoch_params` - [x] `pool_registrations`/`pool_deregistrations` - [x] `reward_withdrawals`/`treasury_withdrawals` - [x] All script-related endpoints - [x] Update examples - [x] Check re-usability - [x] Check schemathesis - [x] guild - [x] preview - [x] preprod - [x] mainnet - [x] Add v0 vs v1 to monitoring - [x] guild - [ ] preview (post merge) - [ ] preprod (post merge) - [ ] mainnet (post 1.1.0 - instead of 1.1.0rc) release - [x] Ogmios - [x] Add integration for tx Evaluation and submission using ogmios path - [x] Add health check based on /health (`version` and `networkSynchronization`) - [x] Update CHANGELOG - [x] Bump final specs version number to v1 ## Which issue it fixes? - [x] Closes #208 - [x] Closes #218 - [x] Closes #186 - [x] Closes #221 - [x] Closes #224 - [x] Closes #191 - [x] Closes #232 - [x] Closes #240 #241 --------- Co-authored-by: KoT_B_KocMoce <49576827+hodlonaut@users.noreply.github.com> Co-authored-by: Ola [AHLNET] --- README.md | 20 +- .../cron/jobs/epoch-info-cache-update.sh | 4 +- .../jobs/epoch-summary-corrections-update.sh | 12 + .../rpc/00_blockchain/reserve_withdrawals.sql | 27 + .../00_blockchain/treasury_withdrawals.sql | 27 + .../rpc/01_cached_tables/asset_info_cache.sql | 8 +- .../01_cached_tables/asset_registry_cache.sql | 2 +- .../rpc/01_cached_tables/epoch_info_cache.sql | 94 +- .../01_cached_tables/pool_history_cache.sql | 10 +- .../rpc/01_cached_tables/pool_info_cache.sql | 12 +- .../stake_distribution_cache.sql | 2 +- files/grest/rpc/02_indexes/13_1_00.sql | 2 +- files/grest/rpc/account/account_addresses.sql | 16 +- files/grest/rpc/account/account_assets.sql | 62 +- files/grest/rpc/account/account_info.sql | 13 +- files/grest/rpc/account/account_txs.sql | 51 + files/grest/rpc/account/account_updates.sql | 2 +- files/grest/rpc/account/account_utxos.sql | 100 +- files/grest/rpc/address/address_assets.sql | 63 +- files/grest/rpc/address/address_info.sql | 50 +- files/grest/rpc/address/address_txs.sql | 43 +- files/grest/rpc/address/address_utxos.sql | 94 ++ files/grest/rpc/address/credential_txs.sql | 48 +- files/grest/rpc/address/credential_utxos.sql | 94 +- files/grest/rpc/assets/asset_addresses.sql | 33 +- files/grest/rpc/assets/asset_history.sql | 24 +- files/grest/rpc/assets/asset_info_bulk.sql | 52 +- files/grest/rpc/assets/asset_nft_address.sql | 22 +- files/grest/rpc/assets/asset_summary.sql | 61 +- files/grest/rpc/assets/asset_txs.sql | 27 +- files/grest/rpc/assets/asset_utxos.sql | 106 ++ .../rpc/assets/policy_asset_addresses.sql | 42 +- files/grest/rpc/assets/policy_asset_info.sql | 37 +- files/grest/rpc/assets/policy_asset_list.sql | 8 +- files/grest/rpc/blocks/block_txs.sql | 19 +- files/grest/rpc/epoch/epoch_info.sql | 7 +- files/grest/rpc/epoch/epoch_params.sql | 119 +- .../epoch_summary_corrections_update.sql | 65 + files/grest/rpc/pool/pool_delegators.sql | 4 +- files/grest/rpc/pool/pool_history.sql | 4 +- files/grest/rpc/pool/pool_info.sql | 87 +- files/grest/rpc/pool/pool_list.sql | 69 +- files/grest/rpc/pool/pool_metadata.sql | 24 +- files/grest/rpc/pool/pool_registrations.sql | 28 + files/grest/rpc/pool/pool_relays.sql | 20 +- files/grest/rpc/pool/pool_retirements.sql | 28 + files/grest/rpc/pool/pool_updates.sql | 91 +- files/grest/rpc/script/datum_info.sql | 17 +- files/grest/rpc/script/native_script_list.sql | 20 +- files/grest/rpc/script/plutus_script_list.sql | 18 +- files/grest/rpc/script/script_info.sql | 32 + files/grest/rpc/script/script_utxos.sql | 81 ++ files/grest/rpc/transactions/tx_info.sql | 8 +- files/grest/rpc/transactions/tx_utxos.sql | 6 +- files/grest/rpc/transactions/utxo_info.sql | 113 ++ html/index.html | 2 +- specs/results/koiosapi-guild.yaml | 1060 ++++++++++++----- specs/results/koiosapi-mainnet.yaml | 1060 ++++++++++++----- specs/results/koiosapi-preprod.yaml | 1056 +++++++++++----- specs/results/koiosapi-preview.yaml | 1056 +++++++++++----- specs/templates/1-api-info.yaml | 24 +- specs/templates/2-api-params.yaml | 10 + specs/templates/3-api-requestBodies.yaml | 118 ++ specs/templates/4-api-schemas.yaml | 389 +++--- specs/templates/api-main.yaml | 507 +++++--- specs/templates/example-map.json | 32 +- tests/conftest.py | 4 +- tests/setup-tests.sh | 6 +- 68 files changed, 5196 insertions(+), 2256 deletions(-) create mode 100644 files/grest/cron/jobs/epoch-summary-corrections-update.sh create mode 100644 files/grest/rpc/00_blockchain/reserve_withdrawals.sql create mode 100644 files/grest/rpc/00_blockchain/treasury_withdrawals.sql create mode 100644 files/grest/rpc/account/account_txs.sql create mode 100644 files/grest/rpc/address/address_utxos.sql create mode 100644 files/grest/rpc/assets/asset_utxos.sql create mode 100644 files/grest/rpc/epoch/epoch_summary_corrections_update.sql create mode 100644 files/grest/rpc/pool/pool_registrations.sql create mode 100644 files/grest/rpc/pool/pool_retirements.sql create mode 100644 files/grest/rpc/script/script_info.sql create mode 100644 files/grest/rpc/script/script_utxos.sql create mode 100644 files/grest/rpc/transactions/utxo_info.sql diff --git a/README.md b/README.md index 57bd3a15..7b48637b 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,30 @@ # Koios Artifacts Repository -Various Artifacts related to [Koios project](https://www.koios.rest) management and assets that are used for website, monitoring services as well as public-facing topology files. +Various Artifacts related to [Koios project](https://www.koios.rest) management and assets that are used for website - look at repository map below for further info. +Provisioning scripts to run an instance are part of [guild-operators repo](https://cardano-community.github.io/guild-operators/Build/grest/) alongwith Koios SPO tools ## Repository Map + ``` . ├── grafana-dashboards/ # Grafana dashboards used for monitoring Koios nodes ├── html/ # HTML Page(s) used for https://api.koios.rest website -├── images/ # Images (logos, Design diagram, etc) used for Koios +├── images/ # Images used for website (incl. projects building on Koios) ├── specs/ # Files used for creation of API Specs with examples ├── tests/ # Test suites for Koios nodes ├── topology/ # Topology files of public Koios nodes ├── topology-guild.json ├── topology-mainnet.json - └── topology-testnet.json -├── LICENSE # License for use of artifacts within this repository -├── README.md # This file -└── projects.json # List of projects (in JSON format) that are using Koios + └── topology-preprod.json + └── topology-preview.json +├── LICENSE # License for use of artifacts within this repository +├── README.md # This file +└── projects.json # List of projects (in JSON format) that are using Koios ``` + ## Project Management -Koios team operates gRest layer in a transparent manner and progress/millestones can be accessed at any time (accessible [here](https://github.com/orgs/cardano-community/projects/1)) +Koios team operates gRest layer in a transparent manner and progress/millestones can be accessed at any time (accessible [here](https://github.com/orgs/cardano-community/projects/1/views/2)) ## API Specs @@ -35,4 +39,4 @@ The specs can be browsed for each network using below: ## Further discussions -You can connect and discuss with Koios teams on [Telegram](https://t.me/+zE4Lce_QUepiY2U1), or feel free to contribute to any of the repositories. +You can connect and discuss with Koios teams on [Telegram](https://t.me/CardanoKoios/1), or feel free to contribute to any of the repositories. diff --git a/files/grest/cron/jobs/epoch-info-cache-update.sh b/files/grest/cron/jobs/epoch-info-cache-update.sh index 19012f81..c63aa739 100644 --- a/files/grest/cron/jobs/epoch-info-cache-update.sh +++ b/files/grest/cron/jobs/epoch-info-cache-update.sh @@ -1,12 +1,12 @@ #!/bin/bash DB_NAME=cexplorer -tip=$(psql ${DB_NAME} -qbt -c "select extract(epoch from time)::integer from block order by id desc limit 1;" | xargs) +tip=$(psql ${DB_NAME} -qbt -c "SELECT EXTRACT(EPOCH FROM time)::integer FROM block ORDER BY id DESC LIMIT 1;" | xargs) if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 fi echo "$(date +%F_%H:%M:%S) Running epoch info cache update..." -psql ${DB_NAME} -qbt -c "SELECT GREST.EPOCH_INFO_CACHE_UPDATE();" 1>/dev/null 2>&1 +psql ${DB_NAME} -qbt -c "SELECT grest.epoch_info_cache_update();" 1>/dev/null 2>&1 echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/cron/jobs/epoch-summary-corrections-update.sh b/files/grest/cron/jobs/epoch-summary-corrections-update.sh new file mode 100644 index 00000000..7bc9c9e4 --- /dev/null +++ b/files/grest/cron/jobs/epoch-summary-corrections-update.sh @@ -0,0 +1,12 @@ +#!/bin/bash +DB_NAME=cexplorer + +tip=$(psql ${DB_NAME} -qbt -c "SELECT EXTRACT(EPOCH FROM time)::integer FROM block ORDER BY id DESC LIMIT 1;" | xargs) + +if [[ $(( $(date +%s) - tip )) -gt 300 ]]; then + echo "$(date +%F_%H:%M:%S) Skipping as database has not received a new block in past 300 seconds!" && exit 1 +fi + +echo "$(date +%F_%H:%M:%S) Running epoch summary corrections update..." +psql ${DB_NAME} -qbt -c "SELECT GREST.EPOCH_SUMMARY_CORRECTIONS_UPDATE();" 1>/dev/null 2>&1 +echo "$(date +%F_%H:%M:%S) Job done!" diff --git a/files/grest/rpc/00_blockchain/reserve_withdrawals.sql b/files/grest/rpc/00_blockchain/reserve_withdrawals.sql new file mode 100644 index 00000000..4c0f3c18 --- /dev/null +++ b/files/grest/rpc/00_blockchain/reserve_withdrawals.sql @@ -0,0 +1,27 @@ +CREATE OR REPLACE FUNCTION grest.reserve_withdrawals() +RETURNS TABLE ( + epoch_no word31type, + epoch_slot word31type, + tx_hash text, + block_hash text, + block_height word31type, + amount text, + stake_address text +) +LANGUAGE SQL STABLE +AS $$ + SELECT + b.epoch_no, + b.epoch_slot_no, + ENCODE(tx.hash,'hex'), + ENCODE(b.hash,'hex'), + b.block_no, + r.amount::text, + sa.view + FROM reserve AS r + LEFT JOIN tx ON r.tx_id = tx.id + INNER JOIN block AS b ON tx.block_id = b.id + LEFT JOIN stake_address AS sa ON sa.id = r.addr_id; +$$; + +COMMENT ON FUNCTION grest.reserve_withdrawals IS 'A list of withdrawals made from reserves (MIRs)'; --noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/00_blockchain/treasury_withdrawals.sql b/files/grest/rpc/00_blockchain/treasury_withdrawals.sql new file mode 100644 index 00000000..b9919831 --- /dev/null +++ b/files/grest/rpc/00_blockchain/treasury_withdrawals.sql @@ -0,0 +1,27 @@ +CREATE OR REPLACE FUNCTION grest.treasury_withdrawals() +RETURNS TABLE ( + epoch_no word31type, + epoch_slot word31type, + tx_hash text, + block_hash text, + block_height word31type, + amount text, + stake_address text +) +LANGUAGE SQL STABLE +AS $$ + SELECT + b.epoch_no, + b.epoch_slot_no, + ENCODE(tx.hash,'hex'), + ENCODE(b.hash,'hex'), + b.block_no, + t.amount::text, + sa.view + FROM treasury AS t + LEFT JOIN tx ON t.tx_id = tx.id + INNER JOIN block AS b ON tx.block_id = b.id + LEFT JOIN stake_address AS sa ON sa.id = t.addr_id; +$$; + +COMMENT ON FUNCTION grest.treasury_withdrawals IS 'A list of withdrawals made from treasury'; --noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/01_cached_tables/asset_info_cache.sql b/files/grest/rpc/01_cached_tables/asset_info_cache.sql index 9dd11d16..41be3082 100644 --- a/files/grest/rpc/01_cached_tables/asset_info_cache.sql +++ b/files/grest/rpc/01_cached_tables/asset_info_cache.sql @@ -30,7 +30,7 @@ BEGIN WHERE state = 'active' AND query ILIKE '%grest.asset_info_cache_update%' AND datname = (SELECT current_database()) - ) THEN + ) THEN RAISE EXCEPTION 'Previous asset_info_cache_update query still running but should have completed! Exiting...'; END IF; @@ -56,7 +56,7 @@ BEGIN tx_mint_meta AS ( SELECT mtm.ident, - MIN(mtm.tx_id) AS first_mint_tx_id, + MIN(mtm.tx_id) AS first_mint_tx_id, MAX(mtm.tx_id) AS last_mint_tx_id FROM ma_tx_mint AS mtm INNER JOIN tx_metadata AS tm ON tm.tx_id = mtm.tx_id @@ -74,7 +74,7 @@ BEGIN tx_mint_nometa AS ( SELECT mtm.ident, - MIN(mtm.tx_id) AS first_mint_tx_id, + MIN(mtm.tx_id) AS first_mint_tx_id, MAX(mtm.tx_id) AS last_mint_tx_id FROM ma_tx_mint AS mtm LEFT JOIN tx_mint_meta ON tx_mint_meta.ident = mtm.ident @@ -111,7 +111,7 @@ BEGIN FROM tx_mint_nometa ) - INSERT INTO grest.asset_info_cache + INSERT INTO grest.asset_info_cache SELECT ma.id, MIN(B.time) AS creation_time, diff --git a/files/grest/rpc/01_cached_tables/asset_registry_cache.sql b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql index 7d1ce37f..afc2fbe3 100644 --- a/files/grest/rpc/01_cached_tables/asset_registry_cache.sql +++ b/files/grest/rpc/01_cached_tables/asset_registry_cache.sql @@ -38,7 +38,7 @@ BEGIN decimals ) VALUES( - _asset_policy, + _asset_policy, _asset_name, _name, _description, diff --git a/files/grest/rpc/01_cached_tables/epoch_info_cache.sql b/files/grest/rpc/01_cached_tables/epoch_info_cache.sql index c6a2fc00..c210d664 100644 --- a/files/grest/rpc/01_cached_tables/epoch_info_cache.sql +++ b/files/grest/rpc/01_cached_tables/epoch_info_cache.sql @@ -9,37 +9,8 @@ CREATE TABLE IF NOT EXISTS grest.epoch_info_cache ( i_total_rewards lovelace, i_avg_blk_reward lovelace, i_last_tx_id bigint, - p_min_fee_a word31type, - p_min_fee_b word31type, - p_max_block_size word31type, - p_max_tx_size word31type, - p_max_bh_size word31type, - p_key_deposit lovelace, - p_pool_deposit lovelace, - p_max_epoch word31type, - p_optimal_pool_count word31type, - p_influence double precision, - p_monetary_expand_rate double precision, - p_treasury_growth_rate double precision, - p_decentralisation double precision, - p_extra_entropy text, - p_protocol_major word31type, - p_protocol_minor word31type, - p_min_utxo_value lovelace, - p_min_pool_cost lovelace, p_nonce text, - p_block_hash text, - p_cost_models character varying, - p_price_mem double precision, - p_price_step double precision, - p_max_tx_ex_mem word64type, - p_max_tx_ex_steps word64type, - p_max_block_ex_mem word64type, - p_max_block_ex_steps word64type, - p_max_val_size word64type, - p_collateral_percent word31type, - p_max_collateral_inputs word31type, - p_coins_per_utxo_size lovelace + p_block_hash text ); COMMENT ON TABLE grest.epoch_info_cache IS 'Contains detailed info for epochs including protocol parameters'; @@ -105,6 +76,20 @@ BEGIN DELETE FROM grest.epoch_info_cache WHERE epoch_no >= _epoch_no_to_insert_from; + DROP TABLE IF EXISTS last_tx_id_subset; + CREATE TEMP TABLE last_tx_id_subset ( + epoch_no bigint, + tx_id bigint + ); + + INSERT INTO last_tx_id_subset + SELECT b.epoch_no, MAX(tx.id) + FROM block AS b + INNER JOIN tx ON tx.block_id = b.id + WHERE b.block_no IS NOT NULL + AND b.tx_count != 0 + GROUP BY b.epoch_no; + INSERT INTO grest.epoch_info_cache SELECT DISTINCT ON (b.time) e.no AS epoch_no, @@ -115,45 +100,20 @@ BEGIN EXTRACT(EPOCH FROM e.start_time) AS i_first_block_time, EXTRACT(EPOCH FROM e.end_time) AS i_last_block_time, CASE -- populated in epoch n + 2 - WHEN e.no <= _curr_epoch - 2 THEN reward_pot.amount + WHEN e.no <= _curr_epoch - 2 THEN reward_pot.amount ELSE NULL END AS i_total_rewards, CASE -- populated in epoch n + 2 WHEN e.no <= _curr_epoch THEN ROUND(reward_pot.amount / e.blk_count) ELSE NULL - END AS i_avg_blk_reward, - last_tx.tx_id AS i_last_tx_id, - ep.min_fee_a AS p_min_fee_a, - ep.min_fee_b AS p_min_fee_b, - ep.max_block_size AS p_max_block_size, - ep.max_tx_size AS p_max_tx_size, - ep.max_bh_size AS p_max_bh_size, - ep.key_deposit AS p_key_deposit, - ep.pool_deposit AS p_pool_deposit, - ep.max_epoch AS p_max_epoch, - ep.optimal_pool_count AS p_optimal_pool_count, - ep.influence AS p_influence, - ep.monetary_expand_rate AS p_monetary_expand_rate, - ep.treasury_growth_rate AS p_treasury_growth_rate, - ep.decentralisation AS p_decentralisation, - ENCODE(ep.extra_entropy, 'hex') AS p_extra_entropy, - ep.protocol_major AS p_protocol_major, - ep.protocol_minor AS p_protocol_minor, - ep.min_utxo_value AS p_min_utxo_value, - ep.min_pool_cost AS p_min_pool_cost, + END AS i_avg_blk_reward, + ( + SELECT MAX(tx_id) + FROM last_tx_id_subset + WHERE epoch_no <= e.no + ) AS i_last_tx_id, ENCODE(ep.nonce, 'hex') AS p_nonce, - ENCODE(b.hash, 'hex') AS p_block_hash, - cm.costs AS p_cost_models, - ep.price_mem AS p_price_mem, - ep.price_step AS p_price_step, - ep.max_tx_ex_mem AS p_max_tx_ex_mem, - ep.max_tx_ex_steps AS p_max_tx_ex_steps, - ep.max_block_ex_mem AS p_max_block_ex_mem, - ep.max_block_ex_steps AS p_max_block_ex_steps, - ep.max_val_size AS p_max_val_size, - ep.collateral_percent AS p_collateral_percent, - ep.max_collateral_inputs AS p_max_collateral_inputs, - ep.coins_per_utxo_size AS p_coins_per_utxo_size + ENCODE(b.hash, 'hex') AS p_block_hash FROM epoch AS e LEFT JOIN epoch_param AS ep ON ep.epoch_no = e.no LEFT JOIN cost_model AS cm ON cm.id = ep.cost_model_id @@ -167,14 +127,6 @@ BEGIN GROUP BY e.no ) AS reward_pot ON TRUE - LEFT JOIN LATERAL ( - SELECT MAX(tx.id) AS tx_id - FROM block AS b - INNER JOIN tx ON tx.block_id = b.id - WHERE b.epoch_no <= e.no - AND b.block_no IS NOT NULL - AND b.tx_count != 0 - ) AS last_tx ON TRUE WHERE e.no >= _epoch_no_to_insert_from ORDER BY b.time ASC, diff --git a/files/grest/rpc/01_cached_tables/pool_history_cache.sql b/files/grest/rpc/01_cached_tables/pool_history_cache.sql index 9644e847..e4a0951b 100644 --- a/files/grest/rpc/01_cached_tables/pool_history_cache.sql +++ b/files/grest/rpc/01_cached_tables/pool_history_cache.sql @@ -39,9 +39,9 @@ BEGIN IF ( SELECT COUNT(key) != 1 FROM GREST.CONTROL_TABLE - WHERE key = 'epoch_info_cache_last_updated' + WHERE key = 'last_active_stake_validated_epoch' ) THEN - RAISE EXCEPTION 'Epoch Info Cache not yet populated! Exiting...'; + RAISE EXCEPTION 'Active stake cache not yet populated! Exiting...'; END IF; IF _epoch_no_to_insert_from IS NULL THEN @@ -153,9 +153,9 @@ BEGIN ROUND( (act.amount / ( SELECT supply::bigint / ( - SELECT eic.p_optimal_pool_count - FROM grest.epoch_info_cache AS eic - WHERE eic.epoch_no = act.epoch_no + SELECT ep.optimal_pool_count + FROM epoch_param AS ep + WHERE ep.epoch_no = act.epoch_no ) FROM grest.totals (act.epoch_no) ) * 100 diff --git a/files/grest/rpc/01_cached_tables/pool_info_cache.sql b/files/grest/rpc/01_cached_tables/pool_info_cache.sql index 39beb5f8..ca50c57d 100644 --- a/files/grest/rpc/01_cached_tables/pool_info_cache.sql +++ b/files/grest/rpc/01_cached_tables/pool_info_cache.sql @@ -55,7 +55,7 @@ BEGIN ORDER BY pr.id LIMIT 1; - IF _retiring_epoch IS NULL THEN + IF _retiring_epoch IS NULL THEN _pool_status := 'registered'; ELSIF _retiring_epoch > _current_epoch_no THEN _pool_status := 'retiring'; @@ -69,7 +69,7 @@ BEGIN tx_hash, block_time, pool_hash_id, - pool_id_bech32, + pool_id_bech32, pool_id_hex, active_epoch_no, vrf_key_hash, @@ -88,7 +88,7 @@ BEGIN SELECT _tx_id, _update_id, - encode(tx.hash::bytea, 'hex'), + encode(tx.hash::bytea, 'hex'), EXTRACT(EPOCH FROM b.time), _hash_id, ph.view, @@ -174,7 +174,7 @@ BEGIN ORDER BY pr.id LIMIT 1; - IF _retiring_epoch IS NULL THEN + IF _retiring_epoch IS NULL THEN _pool_status := 'registered'; ELSIF _retiring_epoch > _current_epoch_no THEN _pool_status := 'retiring'; @@ -222,8 +222,8 @@ BEGIN END IF; ELSIF (tg_table_name = 'pool_relay') THEN - SELECT pic.id INTO _latest_pool_update_id - FROM grest.pool_info_cache AS pic + SELECT pic.id INTO _latest_pool_update_id + FROM grest.pool_info_cache AS pic INNER JOIN public.pool_update AS pu ON pu.hash_id = pic.pool_hash_id AND pu.registered_tx_id = pic.tx_id WHERE pu.id = new.update_id; diff --git a/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql b/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql index 74cc44a9..e7d6960f 100644 --- a/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql +++ b/files/grest/rpc/01_cached_tables/stake_distribution_cache.sql @@ -266,7 +266,7 @@ BEGIN SELECT (_current_block_height - _last_update_block_height) INTO _last_update_block_diff; -- Do nothing until there is a 180 blocks difference in height - 60 minutes theoretical time -- 185 in check because last block height considered is 5 blocks behind tip - + Raise NOTICE 'Last stake distribution update was % blocks ago...', _last_update_block_diff; IF (_last_update_block_diff >= 180 diff --git a/files/grest/rpc/02_indexes/13_1_00.sql b/files/grest/rpc/02_indexes/13_1_00.sql index 545c0985..39d471ca 100644 --- a/files/grest/rpc/02_indexes/13_1_00.sql +++ b/files/grest/rpc/02_indexes/13_1_00.sql @@ -5,7 +5,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS unique_col_txout ON public.collateral_tx_out U CREATE UNIQUE INDEX IF NOT EXISTS unique_delegation ON public.delegation USING btree (tx_id, cert_index); CREATE UNIQUE INDEX IF NOT EXISTS unique_epoch_param ON public.epoch_param USING btree (epoch_no, block_id); CREATE UNIQUE INDEX IF NOT EXISTS unique_ma_tx_mint ON public.ma_tx_mint USING btree (ident, tx_id); -CREATE UNIQUE INDEX IF NOT EXISTS unique_ma_tx_out ON public.ma_tx_out USING btree (ident, tx_out_id); +CREATE UNIQUE INDEX IF NOT EXISTS unique_ma_tx_out ON public.ma_tx_out USING btree (ident, tx_out_id DESC); CREATE UNIQUE INDEX IF NOT EXISTS unique_param_proposal ON public.param_proposal USING btree (key, registered_tx_id); CREATE UNIQUE INDEX IF NOT EXISTS unique_pool_owner ON public.pool_owner USING btree (addr_id, pool_update_id); CREATE UNIQUE INDEX IF NOT EXISTS unique_pool_relay ON public.pool_relay USING btree (update_id, ipv4, ipv6, dns_name); diff --git a/files/grest/rpc/account/account_addresses.sql b/files/grest/rpc/account/account_addresses.sql index 51f9a7b6..b3039088 100644 --- a/files/grest/rpc/account/account_addresses.sql +++ b/files/grest/rpc/account/account_addresses.sql @@ -27,13 +27,9 @@ BEGIN txo.address, txo.stake_address_id, txo.id - FROM - tx_out AS txo - LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id - AND txo.index::smallint = tx_in.tx_out_index::smallint - WHERE - txo.stake_address_id = ANY(sa_id_list) - AND tx_in.tx_out_id IS NULL + FROM tx_out AS txo + WHERE txo.stake_address_id = ANY(sa_id_list) + AND txo.consumed_by_tx_in_id IS NULL ) AS x ) @@ -57,10 +53,8 @@ BEGIN txo.address, txo.stake_address_id, txo.id - FROM - tx_out AS txo - WHERE - txo.stake_address_id = ANY(sa_id_list) + FROM tx_out AS txo + WHERE txo.stake_address_id = ANY(sa_id_list) LIMIT (CASE WHEN _first_only IS TRUE THEN 1 ELSE NULL END) ) AS x ) diff --git a/files/grest/rpc/account/account_assets.sql b/files/grest/rpc/account/account_assets.sql index 06397515..0ead9fc8 100644 --- a/files/grest/rpc/account/account_assets.sql +++ b/files/grest/rpc/account/account_assets.sql @@ -1,20 +1,17 @@ CREATE OR REPLACE FUNCTION grest.account_assets(_stake_addresses text []) RETURNS TABLE ( stake_address varchar, - asset_list jsonb + policy_id text, + asset_name text, + fingerprint varchar, + decimals integer, + quantity text ) LANGUAGE plpgsql AS $$ -DECLARE - sa_id_list integer[]; BEGIN - SELECT INTO sa_id_list - ARRAY_AGG(stake_address.id) - FROM - stake_address - WHERE - stake_address.view = ANY(_stake_addresses); RETURN QUERY + WITH _all_assets AS ( SELECT sa.view, @@ -23,42 +20,27 @@ BEGIN ma.fingerprint, COALESCE(aic.decimals, 0) AS decimals, SUM(mtx.quantity) AS quantity - FROM - ma_tx_out AS mtx - INNER JOIN multi_asset AS ma ON ma.id = mtx.ident - INNER JOIN tx_out AS txo ON txo.id = mtx.tx_out_id - INNER JOIN stake_address AS sa ON sa.id = txo.stake_address_id - LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id - AND txo.index::smallint = tx_in.tx_out_index::smallint - WHERE - sa.id = ANY(sa_id_list) - AND tx_in.tx_out_id IS NULL + FROM ma_tx_out AS mtx + INNER JOIN multi_asset AS ma ON ma.id = mtx.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + INNER JOIN tx_out AS txo ON txo.id = mtx.tx_out_id + INNER JOIN stake_address AS sa ON sa.id = txo.stake_address_id + WHERE sa.view = ANY(_stake_addresses) + AND txo.consumed_by_tx_in_id IS NULL GROUP BY sa.view, ma.policy, ma.name, ma.fingerprint, aic.decimals ) SELECT - assets_grouped.view AS stake_address, - assets_grouped.assets - FROM ( - SELECT - aa.view, - JSONB_AGG( - JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(aa.policy, 'hex'), - 'asset_name', ENCODE(aa.name, 'hex'), - 'fingerprint', aa.fingerprint, - 'decimals', COALESCE(aa.decimals, 0), - 'quantity', aa.quantity::text - ) - ) AS assets - FROM - _all_assets AS aa - GROUP BY - aa.view - ) AS assets_grouped; + aa.view AS stake_address, + ENCODE(aa.policy, 'hex') AS policy_id, + ENCODE(aa.name, 'hex') AS asset_name, + aa.fingerprint AS fingerprint, + aa.decimals AS decimals, + aa.quantity::text AS quantity + FROM _all_assets AS aa + ORDER BY aa.view; END; $$; -COMMENT ON FUNCTION grest.account_assets IS 'Get the native asset balance of given accounts'; -- noqa: LT01 +COMMENT ON FUNCTION grest.account_assets IS 'Get the native asset balance of given accounts'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/account/account_info.sql b/files/grest/rpc/account/account_info.sql index e2149b5b..213e3aaa 100644 --- a/files/grest/rpc/account/account_info.sql +++ b/files/grest/rpc/account/account_info.sql @@ -109,15 +109,10 @@ BEGIN SELECT tx_out.stake_address_id, COALESCE(SUM(VALUE), 0) AS utxo - FROM - tx_out - LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id - AND tx_out.index::smallint = tx_in.tx_out_index::smallint - WHERE - tx_out.stake_address_id = ANY(sa_id_list) - AND tx_in.tx_out_id IS NULL - GROUP BY - tx_out.stake_address_id + FROM tx_out + WHERE tx_out.stake_address_id = ANY(sa_id_list) + AND tx_out.consumed_by_tx_in_id IS NULL + GROUP BY tx_out.stake_address_id ) AS utxo_t ON utxo_t.stake_address_id = status_t.id LEFT JOIN ( SELECT diff --git a/files/grest/rpc/account/account_txs.sql b/files/grest/rpc/account/account_txs.sql new file mode 100644 index 00000000..8a1f9d44 --- /dev/null +++ b/files/grest/rpc/account/account_txs.sql @@ -0,0 +1,51 @@ +CREATE OR REPLACE FUNCTION grest.account_txs(_stake_address text, _after_block_height integer DEFAULT 0) +RETURNS TABLE ( + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer +) +LANGUAGE plpgsql +AS $$ +DECLARE + _tx_id_min bigint; + _tx_id_list bigint[]; +BEGIN + SELECT INTO _tx_id_min id + FROM tx + WHERE block_id >= (SELECT id FROM block WHERE block_no >= _after_block_height ORDER BY id limit 1) + ORDER BY id limit 1; + + -- all tx_out & tx_in tx ids + SELECT INTO _tx_id_list ARRAY_AGG(tx_id) + FROM ( + SELECT tx_id + FROM tx_out + WHERE stake_address_id = ANY(SELECT id FROM stake_address WHERE view = _stake_address) + AND tx_id >= _tx_id_min + -- + UNION + -- + SELECT consumed_by_tx_in_id AS tx_id + FROM tx_out + WHERE + tx_out.consumed_by_tx_in_id IS NULL + AND tx_out.stake_address_id = ANY(SELECT id FROM stake_address WHERE view = _stake_address) + AND tx_out.consumed_by_tx_in_id >= _tx_id_min + ) AS tmp; + + RETURN QUERY + SELECT + DISTINCT(ENCODE(tx.hash, 'hex')) AS tx_hash, + b.epoch_no, + b.block_no AS block_height, + EXTRACT(EPOCH FROM b.time)::integer AS block_time + FROM public.tx + INNER JOIN public.block AS b ON b.id = tx.block_id + WHERE tx.id = ANY(_tx_id_list) + AND b.block_no >= _after_block_height + ORDER BY b.block_no DESC; +END; +$$; + +COMMENT ON FUNCTION grest.account_txs IS 'Get transactions associated with a given stake address'; -- noqa: LT01 diff --git a/files/grest/rpc/account/account_updates.sql b/files/grest/rpc/account/account_updates.sql index 371829d6..3a851529 100644 --- a/files/grest/rpc/account/account_updates.sql +++ b/files/grest/rpc/account/account_updates.sql @@ -9,7 +9,7 @@ DECLARE sa_id_list integer[] DEFAULT NULL; BEGIN SELECT INTO sa_id_list - ARRAY_AGG(stake_address.id) + ARRAY_AGG(stake_address.id) FROM stake_address WHERE diff --git a/files/grest/rpc/account/account_utxos.sql b/files/grest/rpc/account/account_utxos.sql index 2692104d..5efa664e 100644 --- a/files/grest/rpc/account/account_utxos.sql +++ b/files/grest/rpc/account/account_utxos.sql @@ -1,34 +1,94 @@ -CREATE OR REPLACE FUNCTION grest.account_utxos(_stake_address text) +CREATE OR REPLACE FUNCTION grest.account_utxos(_stake_addresses text [], _extended boolean DEFAULT false) RETURNS TABLE ( tx_hash text, tx_index smallint, - address varchar, + address text, value text, + stake_address text, + payment_cred text, + epoch_no word31type, block_height word31type, - block_time integer + block_time integer, + datum_hash text, + inline_datum jsonb, + reference_script jsonb, + asset_list jsonb, + is_spent boolean ) LANGUAGE plpgsql AS $$ +DECLARE + known_addresses varchar[]; BEGIN RETURN QUERY - SELECT - ENCODE(tx.hash,'hex') AS tx_hash, - tx_out.index::smallint AS tx_index, - tx_out.address, - tx_out.value::text AS value, - b.block_no AS block_height, - EXTRACT(EPOCH FROM b.time)::integer AS block_time - FROM - tx_out - LEFT JOIN tx_in ON tx_in.tx_out_id = tx_out.tx_id - AND tx_in.tx_out_index = tx_out.index - INNER JOIN tx ON tx.id = tx_out.tx_id + WITH + _assets AS ( + SELECT + txo.id, + JSONB_AGG(CASE WHEN ma.policy IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, + 'decimals', aic.decimals, + 'quantity', mto.quantity::text + ) + END) as assets + FROM tx_out AS txo + INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txo.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE txo.stake_address_id IN (SELECT sa.id FROM stake_address AS sa WHERE sa.view = ANY(_stake_addresses)) + AND txo.consumed_by_tx_in_id IS NULL + GROUP BY txo.id + ) + SELECT + ENCODE(tx.hash, 'hex')::text AS tx_hash, + tx_out.index::smallint, + tx_out.address::text, + tx_out.value::text, + sa.view::text as stake_address, + ENCODE(tx_out.payment_cred, 'hex') AS payment_cred, + b.epoch_no, + b.block_no, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE + WHEN _extended = false OR tx_out.inline_datum_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END) AS inline_datum, + (CASE + WHEN _extended = false OR tx_out.reference_script_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END) AS reference_script, + CASE + WHEN _extended = false THEN NULL + ELSE COALESCE(assets, JSONB_BUILD_ARRAY()) + END AS asset_list, + (CASE + WHEN tx_out.consumed_by_tx_in_id IS NULL THEN false + ELSE true + END) AS is_spent + FROM tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id LEFT JOIN block AS b ON b.id = tx.block_id - WHERE - tx_in.tx_out_id IS NULL - AND - tx_out.stake_address_id = (SELECT id FROM stake_address WHERE view = _stake_address); + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN _assets ON tx_out.id = _assets.id + WHERE tx_out.stake_address_id IN (SELECT sa.id FROM stake_address AS sa WHERE sa.view = ANY(_stake_addresses)) + AND tx_out.consumed_by_tx_in_id IS NULL + ; END; $$; -COMMENT ON FUNCTION grest.account_utxos IS 'Get non-empty UTxOs associated with a given stake address'; -- noqa: LT01 +COMMENT ON FUNCTION grest.account_utxos IS 'Get UTxO details for requested stake account'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/address/address_assets.sql b/files/grest/rpc/address/address_assets.sql index badacf1d..ea1bea11 100644 --- a/files/grest/rpc/address/address_assets.sql +++ b/files/grest/rpc/address/address_assets.sql @@ -1,55 +1,44 @@ CREATE OR REPLACE FUNCTION grest.address_assets(_addresses text []) RETURNS TABLE ( address varchar, - asset_list jsonb + policy_id text, + asset_name text, + fingerprint varchar, + decimals integer, + quantity text ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY - WITH _all_assets AS ( - SELECT - txo.address, - ma.policy, - ma.name, - ma.fingerprint, - COALESCE(aic.decimals, 0) AS decimals, - SUM(mtx.quantity) AS quantity - FROM - ma_tx_out AS mtx + WITH _all_assets AS ( + SELECT + txo.address, + ma.policy, + ma.name, + ma.fingerprint, + COALESCE(aic.decimals, 0) AS decimals, + SUM(mtx.quantity) AS quantity + FROM ma_tx_out AS mtx INNER JOIN multi_asset AS ma ON ma.id = mtx.ident LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id INNER JOIN tx_out AS txo ON txo.id = mtx.tx_out_id - LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id - AND txo.index::smallint = tx_in.tx_out_index::smallint - WHERE - txo.address = ANY(_addresses) - AND tx_in.tx_out_id IS NULL - GROUP BY - txo.address, ma.policy, ma.name, ma.fingerprint, aic.decimals - ) + WHERE txo.address = ANY(_addresses) + AND txo.consumed_by_tx_in_id IS NULL + GROUP BY + txo.address, ma.policy, ma.name, ma.fingerprint, aic.decimals + ) - SELECT - assets_grouped.address, - assets_grouped.asset_list - FROM ( SELECT aa.address, - JSONB_AGG( - JSONB_BUILD_OBJECT( - 'policy_id', ENCODE(aa.policy, 'hex'), - 'asset_name', ENCODE(aa.name, 'hex'), - 'fingerprint', aa.fingerprint, - 'decimals', aa.decimals, - 'quantity', aa.quantity::text - ) - ) AS asset_list - FROM - _all_assets AS aa - GROUP BY - aa.address - ) assets_grouped; + ENCODE(aa.policy, 'hex') AS policy_id, + ENCODE(aa.name, 'hex') AS asset_name, + aa.fingerprint AS fingerprint, + aa.decimals AS decimals, + aa.quantity::text AS quantity + FROM _all_assets AS aa + ORDER BY aa.address; END; $$; diff --git a/files/grest/rpc/address/address_info.sql b/files/grest/rpc/address/address_info.sql index e4388cba..3c4ee367 100644 --- a/files/grest/rpc/address/address_info.sql +++ b/files/grest/rpc/address/address_info.sql @@ -16,11 +16,9 @@ BEGIN DISTINCT ON (tx_out.address) tx_out.address, sa.view AS stake_address, COALESCE(tx_out.address_has_script, 'false') AS script_address - FROM - tx_out - LEFT JOIN stake_address sa ON sa.id = tx_out.stake_address_id - WHERE - tx_out.address = ANY(_addresses); + FROM tx_out + LEFT JOIN stake_address AS sa ON sa.id = tx_out.stake_address_id + WHERE tx_out.address = ANY(_addresses); RETURN QUERY WITH _all_utxos AS ( @@ -35,15 +33,10 @@ BEGIN tx_out.data_hash, tx_out.inline_datum_id, tx_out.reference_script_id - FROM - tx_out - LEFT JOIN tx_in ON tx_in.tx_out_id = tx_out.tx_id - AND tx_in.tx_out_index = tx_out.index - INNER JOIN tx ON tx.id = tx_out.tx_id - WHERE - tx_in.tx_out_id IS NULL - AND - tx_out.address = ANY(_addresses) + FROM tx_out + INNER JOIN tx ON tx.id = tx_out.tx_id + WHERE tx_out.consumed_by_tx_in_id IS NULL + AND tx_out.address = ANY(_addresses) ) SELECT @@ -57,7 +50,7 @@ BEGIN ) THEN JSONB_AGG( JSONB_BUILD_OBJECT( - 'tx_hash', ENCODE(au.hash, 'hex'), + 'tx_hash', ENCODE(au.hash, 'hex'), 'tx_index', au.index, 'block_height', block.block_no, 'block_time', EXTRACT(EPOCH FROM block.time)::integer, @@ -98,12 +91,10 @@ BEGIN 'decimals', COALESCE(aic.decimals, 0), 'quantity', mtx.quantity::text )) - FROM - ma_tx_out AS mtx - INNER JOIN multi_asset AS ma ON ma.id = mtx.ident - LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - WHERE - mtx.tx_out_id = au.txo_id + FROM ma_tx_out AS mtx + INNER JOIN multi_asset AS ma ON ma.id = mtx.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE mtx.tx_out_id = au.txo_id ), JSONB_BUILD_ARRAY() ) @@ -112,16 +103,17 @@ BEGIN ELSE '[]'::jsonb END AS utxo_set - FROM - _known_addresses AS ka - LEFT OUTER JOIN _all_utxos AS au ON au.address = ka.address - LEFT JOIN public.block ON block.id = au.block_id - LEFT JOIN datum ON datum.id = au.inline_datum_id - LEFT JOIN script ON script.id = au.reference_script_id + FROM _known_addresses AS ka + LEFT OUTER JOIN _all_utxos AS au ON au.address = ka.address + LEFT JOIN public.block ON block.id = au.block_id + LEFT JOIN datum ON datum.id = au.inline_datum_id + LEFT JOIN script ON script.id = au.reference_script_id GROUP BY - ka.address, ka.stake_address, ka.script_address; + ka.address, + ka.stake_address, + ka.script_address; DROP TABLE _known_addresses; END; $$; -COMMENT ON FUNCTION grest.address_info IS 'Get bulk address info - balance, associated stake address (if any) and UTXO set'; -- noqa: LT01 +COMMENT ON FUNCTION grest.address_info IS 'Get bulk address info - balance, associated stake address (if any) and UTXO set'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/address/address_txs.sql b/files/grest/rpc/address/address_txs.sql index 840be398..dee58b22 100644 --- a/files/grest/rpc/address/address_txs.sql +++ b/files/grest/rpc/address/address_txs.sql @@ -19,42 +19,33 @@ BEGIN -- all tx_out & tx_in tx ids SELECT INTO _tx_id_list ARRAY_AGG(tx_id) FROM ( - SELECT - tx_id - FROM - tx_out - WHERE - address = ANY(_addresses) + SELECT tx_id + FROM tx_out + WHERE address = ANY(_addresses) AND tx_id >= _tx_id_min -- UNION -- - SELECT - tx_in_id AS tx_id - FROM - tx_out - LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id - AND tx_out.index = tx_in.tx_out_index - WHERE - tx_in.tx_in_id IS NOT NULL + SELECT consumed_by_tx_in_id AS tx_id + FROM tx_out + LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id + AND tx_out.index = tx_in.tx_out_index + WHERE tx_out.consumed_by_tx_in_id IS NOT NULL AND tx_out.address = ANY(_addresses) - AND tx_in.tx_in_id >= _tx_id_min + AND tx_out.consumed_by_tx_in_id >= _tx_id_min ) AS tmp; RETURN QUERY SELECT DISTINCT(ENCODE(tx.hash, 'hex')) AS tx_hash, - block.epoch_no, - block.block_no, - EXTRACT(EPOCH FROM block.time)::integer - FROM - public.tx - INNER JOIN public.block ON block.id = tx.block_id - WHERE - tx.id = ANY(_tx_id_list) - AND block.block_no >= _after_block_height - ORDER BY - block.block_no DESC; + b.epoch_no, + b.block_no AS block_height, + EXTRACT(EPOCH FROM b.time)::integer AS block_time + FROM public.tx + INNER JOIN public.block AS b ON b.id = tx.block_id + WHERE tx.id = ANY(_tx_id_list) + AND b.block_no >= _after_block_height + ORDER BY b.block_no DESC; END; $$; diff --git a/files/grest/rpc/address/address_utxos.sql b/files/grest/rpc/address/address_utxos.sql new file mode 100644 index 00000000..e0a322d6 --- /dev/null +++ b/files/grest/rpc/address/address_utxos.sql @@ -0,0 +1,94 @@ +CREATE OR REPLACE FUNCTION grest.address_utxos(_addresses text [], _extended boolean DEFAULT false) +RETURNS TABLE ( + tx_hash text, + tx_index smallint, + address text, + value text, + stake_address text, + payment_cred text, + epoch_no word31type, + block_height word31type, + block_time integer, + datum_hash text, + inline_datum jsonb, + reference_script jsonb, + asset_list jsonb, + is_spent boolean +) +LANGUAGE plpgsql +AS $$ +DECLARE + known_addresses varchar[]; +BEGIN + RETURN QUERY + WITH + _assets AS ( + SELECT + txo.id, + JSONB_AGG(CASE WHEN ma.policy IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, + 'decimals', aic.decimals, + 'quantity', mto.quantity::text + ) + END) as assets + FROM tx_out AS txo + INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txo.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE txo.address = ANY(_addresses) + AND txo.consumed_by_tx_in_id IS NULL + GROUP BY txo.id + ) + SELECT + ENCODE(tx.hash, 'hex')::text AS tx_hash, + tx_out.index::smallint, + tx_out.address::text, + tx_out.value::text, + sa.view::text as stake_address, + ENCODE(tx_out.payment_cred, 'hex') AS payment_cred, + b.epoch_no, + b.block_no, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE + WHEN _extended = false OR tx_out.inline_datum_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END) AS inline_datum, + (CASE + WHEN _extended = false OR tx_out.reference_script_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END) AS reference_script, + CASE + WHEN _extended = false THEN NULL + ELSE COALESCE(assets, JSONB_BUILD_ARRAY()) + END AS asset_list, + (CASE + WHEN tx_out.consumed_by_tx_in_id IS NULL THEN false + ELSE true + END) AS is_spent + FROM tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN block AS b ON b.id = tx.block_id + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN _assets ON tx_out.id = _assets.id + WHERE tx_out.address = ANY(_addresses) + AND tx_out.consumed_by_tx_in_id IS NULL + ; +END; +$$; + +COMMENT ON FUNCTION grest.address_utxos IS 'Get UTxO details for requested addresses'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/address/credential_txs.sql b/files/grest/rpc/address/credential_txs.sql index d581858a..21763479 100644 --- a/files/grest/rpc/address/credential_txs.sql +++ b/files/grest/rpc/address/credential_txs.sql @@ -16,53 +16,43 @@ BEGIN SELECT INTO _payment_cred_bytea ARRAY_AGG(cred_bytea) FROM ( SELECT DECODE(cred_hex, 'hex') AS cred_bytea - FROM - UNNEST(_payment_credentials) AS cred_hex + FROM UNNEST(_payment_credentials) AS cred_hex ) AS tmp; SELECT INTO _tx_id_min id - FROM tx - WHERE block_id >= (SELECT id FROM block WHERE block_no = _after_block_height) - ORDER BY id limit 1; + FROM tx + WHERE block_id >= (SELECT id FROM block WHERE block_no >= _after_block_height ORDER BY id limit 1) + ORDER BY id limit 1; -- all tx_out & tx_in tx ids SELECT INTO _tx_id_list ARRAY_AGG(tx_id) FROM ( SELECT tx_id - FROM - tx_out - WHERE - payment_cred = ANY(_payment_cred_bytea) + FROM tx_out + WHERE payment_cred = ANY(_payment_cred_bytea) AND tx_id >= _tx_id_min -- UNION -- - SELECT tx_in_id AS tx_id - FROM - tx_out - LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id - AND tx_out.index = tx_in.tx_out_index - WHERE - tx_in.tx_in_id IS NOT NULL + SELECT consumed_by_tx_in_id AS tx_id + FROM tx_out + WHERE tx_out.consumed_by_tx_in_id IS NOT NULL AND tx_out.payment_cred = ANY(_payment_cred_bytea) - AND tx_in.tx_in_id >= _tx_id_min + AND tx_out.consumed_by_tx_in_id >= _tx_id_min ) AS tmp; RETURN QUERY SELECT DISTINCT(ENCODE(tx.hash, 'hex')) AS tx_hash, - block.epoch_no, - block.block_no, - EXTRACT(EPOCH FROM block.time)::integer - FROM - public.tx - INNER JOIN public.block ON block.id = tx.block_id - WHERE - tx.id = ANY(_tx_id_list) - AND block.block_no >= _after_block_height - ORDER BY - block.block_no DESC; + b.epoch_no, + b.block_no AS block_height, + EXTRACT(EPOCH FROM b.time)::integer AS block_time + FROM public.tx + INNER JOIN public.block AS b ON b.id = tx.block_id + WHERE tx.id = ANY(_tx_id_list) + AND b.block_no >= _after_block_height + ORDER BY b.block_no DESC; END; $$; -COMMENT ON FUNCTION grest.address_txs IS 'Get the transaction hash list of a payment credentials array, optionally filtering after specified block height (inclusive).'; --noqa: LT01 +COMMENT ON FUNCTION grest.credential_txs IS 'Get the transaction hash list of a payment credentials array, optionally filtering after specified block height (inclusive).'; --noqa: LT01 diff --git a/files/grest/rpc/address/credential_utxos.sql b/files/grest/rpc/address/credential_utxos.sql index 6d9d4677..8d4d4520 100644 --- a/files/grest/rpc/address/credential_utxos.sql +++ b/files/grest/rpc/address/credential_utxos.sql @@ -1,34 +1,100 @@ -CREATE OR REPLACE FUNCTION grest.credential_utxos(_payment_credentials text []) +CREATE OR REPLACE FUNCTION grest.credential_utxos(_payment_credentials text [], _extended boolean DEFAULT false) RETURNS TABLE ( tx_hash text, tx_index smallint, - value text + address text, + value text, + stake_address text, + payment_cred text, + epoch_no word31type, + block_height word31type, + block_time integer, + datum_hash text, + inline_datum jsonb, + reference_script jsonb, + asset_list jsonb, + is_spent boolean ) LANGUAGE plpgsql AS $$ DECLARE _payment_cred_bytea bytea[]; - BEGIN SELECT INTO _payment_cred_bytea ARRAY_AGG(cred_bytea) FROM ( - SELECT - DECODE(cred_hex, 'hex') AS cred_bytea - FROM - UNNEST(_payment_credentials) AS cred_hex + SELECT DECODE(cred_hex, 'hex') AS cred_bytea + FROM UNNEST(_payment_credentials) AS cred_hex ) AS tmp; RETURN QUERY + WITH + _assets AS ( + SELECT + txo.id, + JSONB_AGG(CASE WHEN ma.policy IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, + 'decimals', aic.decimals, + 'quantity', mto.quantity::text + ) + END) as assets + FROM tx_out AS txo + INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txo.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE txo.payment_cred = ANY(_payment_cred_bytea) + AND txo.consumed_by_tx_in_id IS NULL + GROUP BY txo.id + ) SELECT ENCODE(tx.hash, 'hex')::text AS tx_hash, tx_out.index::smallint, - tx_out.value::text AS balance + tx_out.address::text, + tx_out.value::text, + sa.view::text as stake_address, + ENCODE(tx_out.payment_cred, 'hex') AS payment_cred, + b.epoch_no, + b.block_no, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE + WHEN _extended = false OR tx_out.inline_datum_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END) AS inline_datum, + (CASE + WHEN _extended = false OR tx_out.reference_script_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END) AS reference_script, + CASE + WHEN _extended = false THEN NULL + ELSE COALESCE(assets, JSONB_BUILD_ARRAY()) + END AS asset_list, + (CASE + WHEN tx_out.consumed_by_tx_in_id IS NULL THEN false + ELSE true + END) AS is_spent FROM tx_out - INNER JOIN tx ON tx_out.tx_id = tx.id - LEFT JOIN tx_in ON tx_out.tx_id = tx_in.tx_out_id - AND tx_out.index = tx_in.tx_out_index - WHERE - payment_cred = ANY(_payment_cred_bytea) - AND tx_in.tx_out_id IS NULL; + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN block AS b ON b.id = tx.block_id + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN _assets ON tx_out.id = _assets.id + WHERE tx_out.payment_cred = ANY(_payment_cred_bytea) + AND tx_out.consumed_by_tx_in_id IS NULL + ; END; $$; + +COMMENT ON FUNCTION grest.credential_utxos IS 'Get UTxO details for requested payment credentials'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/assets/asset_addresses.sql b/files/grest/rpc/assets/asset_addresses.sql index 3344d395..dd6d605d 100644 --- a/files/grest/rpc/assets/asset_addresses.sql +++ b/files/grest/rpc/assets/asset_addresses.sql @@ -24,15 +24,15 @@ DECLARE _asset_id int; BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - SELECT DECODE( - CASE WHEN _asset_name IS NULL - THEN '' - ELSE - _asset_name - END, - 'hex' - ) INTO _asset_name_decoded; - SELECT id INTO _asset_id FROM multi_asset AS ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; + SELECT DECODE(CASE + WHEN _asset_name IS NULL THEN '' + ELSE _asset_name + END, 'hex') INTO _asset_name_decoded; + SELECT id INTO _asset_id + FROM multi_asset AS ma + WHERE ma.policy = _asset_policy_decoded + AND ma.name = _asset_name_decoded; + RETURN QUERY SELECT x.address, @@ -42,17 +42,12 @@ BEGIN SELECT txo.address, mto.quantity - FROM - ma_tx_out AS mto - INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id - LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id - AND txo.index::smallint = tx_in.tx_out_index::smallint - WHERE - mto.ident = _asset_id - AND tx_in.tx_out_id IS NULL + FROM ma_tx_out AS mto + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id + WHERE mto.ident = _asset_id + AND txo.consumed_by_tx_in_id IS NULL ) AS x - GROUP BY - x.address; + GROUP BY x.address; END; $$; diff --git a/files/grest/rpc/assets/asset_history.sql b/files/grest/rpc/assets/asset_history.sql index cef2e8b5..77833621 100644 --- a/files/grest/rpc/assets/asset_history.sql +++ b/files/grest/rpc/assets/asset_history.sql @@ -13,10 +13,8 @@ DECLARE BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; SELECT DECODE( - CASE WHEN _asset_name IS NULL - THEN '' - ELSE - _asset_name + CASE WHEN _asset_name IS NULL THEN '' + ELSE _asset_name END, 'hex' ) INTO _asset_name_decoded; @@ -48,16 +46,15 @@ BEGIN 'key', tm.key::text, 'json', tm.json ) - ) + ) END ) AS metadata - FROM - ma_tx_mint AS mtm - INNER JOIN multi_asset AS ma ON ma.id = mtm.ident - INNER JOIN tx ON tx.id = MTM.tx_id - INNER JOIN block AS b ON b.id = tx.block_id - LEFT JOIN tx_metadata AS tm ON tm.tx_id = tx.id - WHERE ma.policy = _asset_policy_decoded + FROM ma_tx_mint AS mtm + INNER JOIN multi_asset AS ma ON ma.id = mtm.ident + INNER JOIN tx ON tx.id = MTM.tx_id + INNER JOIN block AS b ON b.id = tx.block_id + LEFT JOIN tx_metadata AS tm ON tm.tx_id = tx.id + WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded GROUP BY ma.fingerprint, @@ -66,8 +63,7 @@ BEGIN mtm.quantity, tm.key ) AS minting_data - GROUP BY - minting_data.fingerprint; + GROUP BY minting_data.fingerprint; END; $$; diff --git a/files/grest/rpc/assets/asset_info_bulk.sql b/files/grest/rpc/assets/asset_info_bulk.sql index f03c37c1..7e079c97 100644 --- a/files/grest/rpc/assets/asset_info_bulk.sql +++ b/files/grest/rpc/assets/asset_info_bulk.sql @@ -10,7 +10,8 @@ RETURNS TABLE ( burn_cnt bigint, creation_time integer, minting_tx_metadata jsonb, - token_registry_metadata jsonb + token_registry_metadata jsonb, + cip68_metadata jsonb ) LANGUAGE plpgsql AS $$ @@ -52,7 +53,8 @@ BEGIN 'logo', arc.logo, 'decimals', arc.decimals ) - END + END, + cip68.metadata FROM multi_asset AS ma INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id @@ -69,6 +71,52 @@ BEGIN WHERE tm.tx_id = tx.id ) metadata ON TRUE + LEFT JOIN LATERAL ( + -- CIP-68 supported labels + -- 100 = 000643b0 (ref, metadata) + -- 222 = 000de140 (NFT) + -- 333 = 0014df10 (FT) + -- 444 = 001bc280 (RFT) + SELECT + CASE + WHEN datum.value IS NULL THEN NULL + ELSE + JSONB_BUILD_OBJECT( + CASE + WHEN STARTS_WITH(ENCODE(ma.name, 'hex'), '000de140') THEN '222' + WHEN STARTS_WITH(ENCODE(ma.name, 'hex'), '0014df10') THEN '333' + WHEN STARTS_WITH(ENCODE(ma.name, 'hex'), '001bc280') THEN '444' + ELSE '0' + END, + datum.value + ) + END AS metadata + FROM + tx_out + INNER JOIN datum ON datum.hash = tx_out.data_hash + WHERE + tx_out.id = ( + SELECT + (SELECT MAX(tx_out_id) FROM ma_tx_out WHERE ident = _ma.id) as tx_id + FROM + multi_asset _ma + WHERE + _ma.policy = MA.policy + AND + _ma.name = ( + SELECT + CASE WHEN + STARTS_WITH(ENCODE(ma.name, 'hex'), '000de140') + OR + STARTS_WITH(ENCODE(ma.name, 'hex'), '0014df10') + OR + STARTS_WITH(ENCODE(ma.name, 'hex'), '001bc280') + THEN CONCAT('\x000643b0', SUBSTRING(ENCODE(ma.name, 'hex'), 9))::bytea + ELSE null + END + ) + ) + ) cip68 ON TRUE WHERE ma.id = ANY(_asset_id_list); diff --git a/files/grest/rpc/assets/asset_nft_address.sql b/files/grest/rpc/assets/asset_nft_address.sql index daaedab2..221d6cc4 100644 --- a/files/grest/rpc/assets/asset_nft_address.sql +++ b/files/grest/rpc/assets/asset_nft_address.sql @@ -10,27 +10,27 @@ DECLARE BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; SELECT DECODE( - CASE WHEN _asset_name IS NULL - THEN '' - ELSE - _asset_name + CASE WHEN _asset_name IS NULL THEN '' + ELSE _asset_name END, 'hex' ) INTO _asset_name_decoded; - SELECT id INTO _asset_id - FROM - multi_asset AS ma - INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - WHERE - ma.policy = _asset_policy_decoded + SELECT id INTO _asset_id + FROM multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded AND aic.total_supply = 1; RETURN QUERY SELECT address FROM tx_out - WHERE id = (SELECT MAX(tx_out_id) FROM ma_tx_out WHERE ident = _asset_id); + WHERE id = ( + SELECT MAX(tx_out_id) + FROM ma_tx_out + WHERE ident = _asset_id + ); END; $$; diff --git a/files/grest/rpc/assets/asset_summary.sql b/files/grest/rpc/assets/asset_summary.sql index 04ae7d96..68ac5ba8 100644 --- a/files/grest/rpc/assets/asset_summary.sql +++ b/files/grest/rpc/assets/asset_summary.sql @@ -16,14 +16,15 @@ DECLARE BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; SELECT DECODE( - CASE WHEN _asset_name IS NULL - THEN '' - ELSE - _asset_name + CASE WHEN _asset_name IS NULL THEN '' + ELSE _asset_name END, 'hex' ) INTO _asset_name_decoded; - SELECT id INTO _asset_id FROM multi_asset AS ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; + SELECT id INTO _asset_id + FROM multi_asset AS ma + WHERE ma.policy = _asset_policy_decoded + AND ma.name = _asset_name_decoded; RETURN QUERY with _asset_utxos AS ( SELECT @@ -32,48 +33,34 @@ BEGIN txo.index AS tx_out_idx, txo.address AS address, txo.stake_address_id AS sa_id - FROM - ma_tx_out AS mto - INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id - LEFT JOIN tx_in AS txi ON txi.tx_out_id = txo.tx_id - WHERE - mto.ident = _asset_id - AND - txi.tx_out_id IS NULL) - + FROM ma_tx_out AS mto + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id + LEFT JOIN tx_in AS txi ON txi.tx_out_id = txo.tx_id + WHERE mto.ident = _asset_id + AND txi.tx_out_id IS NULL) + SELECT _asset_policy, _asset_name, ma.fingerprint, ( - SELECT - COUNT(DISTINCT(txo.tx_id)) - FROM - ma_tx_out mto - INNER JOIN tx_out txo ON txo.id = mto.tx_out_id - WHERE - ident = _asset_id + SELECT COUNT(DISTINCT(txo.tx_id)) + FROM ma_tx_out mto + INNER JOIN tx_out txo ON txo.id = mto.tx_out_id + WHERE ident = _asset_id ) AS total_transactions, ( - SELECT - COUNT(DISTINCT(_asset_utxos.sa_id)) - FROM - _asset_utxos - WHERE - _asset_utxos.sa_id IS NOT NULL + SELECT COUNT(DISTINCT(_asset_utxos.sa_id)) + FROM _asset_utxos + WHERE _asset_utxos.sa_id IS NOT NULL ) AS staked_wallets, ( - SELECT - COUNT(DISTINCT(_asset_utxos.address)) - FROM - _asset_utxos - WHERE - _asset_utxos.sa_id IS NULL + SELECT COUNT(DISTINCT(_asset_utxos.address)) + FROM _asset_utxos + WHERE _asset_utxos.sa_id IS NULL ) AS unstaked_addresses - FROM - multi_asset AS ma - WHERE - ma.id = _asset_id; + FROM multi_asset AS ma + WHERE ma.id = _asset_id; END; $$; diff --git a/files/grest/rpc/assets/asset_txs.sql b/files/grest/rpc/assets/asset_txs.sql index e34fc924..aaa7f6b7 100644 --- a/files/grest/rpc/assets/asset_txs.sql +++ b/files/grest/rpc/assets/asset_txs.sql @@ -19,14 +19,15 @@ DECLARE BEGIN SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; SELECT DECODE( - CASE WHEN _asset_name IS NULL - THEN '' - ELSE - _asset_name + CASE + WHEN _asset_name IS NULL THEN '' + ELSE _asset_name END, 'hex' ) INTO _asset_name_decoded; - SELECT id INTO _asset_id FROM multi_asset AS ma WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; + SELECT id INTO _asset_id + FROM multi_asset AS ma + WHERE ma.policy = _asset_policy_decoded AND ma.name = _asset_name_decoded; RETURN QUERY SELECT @@ -35,25 +36,23 @@ BEGIN tx_hashes.block_no, EXTRACT(EPOCH FROM tx_hashes.time)::integer FROM ( - SELECT DISTINCT ON (tx.hash) + SELECT DISTINCT ON (tx.hash,txo.index::smallint) tx.hash, block.epoch_no, block.block_no, block.time - FROM - ma_tx_out AS mto - INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id - INNER JOIN tx ON tx.id = txo.tx_id - INNER JOIN block ON block.id = tx.block_id - LEFT JOIN tx_in AS txi ON txo.tx_id = txi.tx_out_id - AND txo.index::smallint = txi.tx_out_index::smallint + FROM ma_tx_out AS mto + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id + INNER JOIN tx ON tx.id = txo.tx_id + INNER JOIN block ON block.id = tx.block_id WHERE mto.ident = _asset_id AND block.block_no >= _after_block_height - AND (_history = TRUE OR txi.id IS NULL) + AND (_history = TRUE OR txo.consumed_by_tx_in_id IS NULL) GROUP BY ident, tx.hash, + txo.index::smallint, block.epoch_no, block.block_no, block.time diff --git a/files/grest/rpc/assets/asset_utxos.sql b/files/grest/rpc/assets/asset_utxos.sql new file mode 100644 index 00000000..d91d2785 --- /dev/null +++ b/files/grest/rpc/assets/asset_utxos.sql @@ -0,0 +1,106 @@ +CREATE OR REPLACE FUNCTION grest.asset_utxos(_asset_list text [] [], _extended boolean DEFAULT false) +RETURNS TABLE ( + tx_hash text, + tx_index smallint, + address text, + value text, + stake_address text, + payment_cred text, + epoch_no word31type, + block_height word31type, + block_time integer, + datum_hash text, + inline_datum jsonb, + reference_script jsonb, + asset_list jsonb, + is_spent boolean +) +LANGUAGE plpgsql +AS $$ +DECLARE + _asset_id_list bigint[]; +BEGIN + -- find all asset id's based ON nested array input + SELECT INTO _asset_id_list ARRAY_AGG(id) + FROM ( + SELECT DISTINCT mu.id + FROM ( + SELECT + DECODE(al->>0, 'hex') AS policy, + DECODE(al->>1, 'hex') AS name + FROM JSONB_ARRAY_ELEMENTS(TO_JSONB(_asset_list)) AS al + ) AS ald + INNER JOIN multi_asset AS mu ON mu.policy = ald.policy AND mu.name = ald.name + ) AS tmp; + + RETURN QUERY + WITH + _assets AS ( + SELECT + txo.id, + JSONB_AGG(CASE WHEN ma.policy IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, + 'decimals', aic.decimals, + 'quantity', mto.quantity::text + ) + END) as assets + FROM tx_out AS txo + INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txo.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE mto.ident = ANY(_asset_id_list) + AND txo.consumed_by_tx_in_id IS NULL + GROUP BY txo.id + ) + SELECT + ENCODE(tx.hash, 'hex')::text AS tx_hash, + tx_out.index::smallint, + tx_out.address::text, + tx_out.value::text, + sa.view::text as stake_address, + ENCODE(tx_out.payment_cred, 'hex') AS payment_cred, + b.epoch_no, + b.block_no, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE + WHEN _extended = false OR tx_out.inline_datum_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END) AS inline_datum, + (CASE + WHEN _extended = false OR tx_out.reference_script_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END) AS reference_script, + CASE + WHEN _extended = false THEN NULL + ELSE COALESCE(assets, JSONB_BUILD_ARRAY()) + END AS asset_list, + (CASE + WHEN tx_out.consumed_by_tx_in_id IS NULL THEN false + ELSE true + END) AS is_spent + FROM tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + INNER JOIN _assets ON tx_out.id = _assets.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN block AS b ON b.id = tx.block_id + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + WHERE tx_out.consumed_by_tx_in_id IS NULL + ; +END; +$$; + +COMMENT ON FUNCTION grest.asset_utxos IS 'Get UTxO details for requested assets'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/assets/policy_asset_addresses.sql b/files/grest/rpc/assets/policy_asset_addresses.sql index 478b1527..0ac2ac4f 100644 --- a/files/grest/rpc/assets/policy_asset_addresses.sql +++ b/files/grest/rpc/assets/policy_asset_addresses.sql @@ -6,42 +6,20 @@ RETURNS TABLE ( ) LANGUAGE plpgsql AS $$ -DECLARE - _asset_policy_decoded bytea; BEGIN - SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; RETURN QUERY - WITH - _all_assets AS ( - SELECT - id, - ENCODE(name, 'hex') AS asset_name - FROM - multi_asset AS ma - WHERE ma.policy = _asset_policy_decoded - ) - SELECT - x.asset_name, - x.address, - SUM(x.quantity)::text - FROM - ( - SELECT - aa.asset_name, - txo.address, - mto.quantity - FROM - _all_assets AS aa - INNER JOIN ma_tx_out AS mto ON mto.ident = aa.id - INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id - LEFT JOIN tx_in ON txo.tx_id = tx_in.tx_out_id - AND txo.index::smallint = tx_in.tx_out_index::smallint - WHERE - tx_in.tx_out_id IS NULL - ) AS x + ENCODE(ma.name, 'hex') AS asset_name, + txo.address, + SUM(mto.quantity)::text + FROM multi_asset AS ma + INNER JOIN ma_tx_out AS mto ON mto.ident = ma.id + INNER JOIN tx_out AS txo ON txo.id = mto.tx_out_id + WHERE ma.policy = DECODE(_asset_policy, 'hex') + AND txo.consumed_by_tx_in_id IS NULL GROUP BY - x.asset_name, x.address; + ma.name, + txo.address; END; $$; diff --git a/files/grest/rpc/assets/policy_asset_info.sql b/files/grest/rpc/assets/policy_asset_info.sql index 5c1e910b..9683479c 100644 --- a/files/grest/rpc/assets/policy_asset_info.sql +++ b/files/grest/rpc/assets/policy_asset_info.sql @@ -34,12 +34,8 @@ RETURNS TABLE ( ) LANGUAGE plpgsql AS $$ -DECLARE - _asset_policy_decoded bytea; - _policy_asset_ids bigint[]; BEGIN - SELECT DECODE(_asset_policy, 'hex') INTO _asset_policy_decoded; - RETURN QUERY + RETURN QUERY SELECT ENCODE(ma.name, 'hex') AS asset_name, ENCODE(ma.name, 'escape') AS asset_name_ascii, @@ -61,24 +57,19 @@ BEGIN 'decimals', arc.decimals ) END - FROM - multi_asset AS ma - INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - INNER JOIN tx ON tx.id = aic.last_mint_tx_id - LEFT JOIN grest.asset_registry_cache AS arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name, 'hex') - LEFT JOIN LATERAL ( - SELECT - JSONB_OBJECT_AGG( - key::text, - json - ) AS minting_tx_metadata - FROM - tx_metadata AS tm - WHERE - tm.tx_id = tx.id - ) metadata ON TRUE - WHERE - ma.policy = _asset_policy_decoded; + FROM multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + INNER JOIN tx ON tx.id = aic.last_mint_tx_id + LEFT JOIN grest.asset_registry_cache AS arc ON arc.asset_policy = ENCODE(ma.policy,'hex') AND arc.asset_name = ENCODE(ma.name, 'hex') + LEFT JOIN LATERAL ( + SELECT JSONB_OBJECT_AGG( + key::text, + json + ) AS minting_tx_metadata + FROM tx_metadata AS tm + WHERE tm.tx_id = tx.id + ) AS metadata ON TRUE + WHERE ma.policy = DECODE(_asset_policy, 'hex'); END; $$; diff --git a/files/grest/rpc/assets/policy_asset_list.sql b/files/grest/rpc/assets/policy_asset_list.sql index 630b9aa2..9893a2ea 100644 --- a/files/grest/rpc/assets/policy_asset_list.sql +++ b/files/grest/rpc/assets/policy_asset_list.sql @@ -17,11 +17,9 @@ BEGIN ma.fingerprint AS fingerprint, aic.total_supply::text, aic.decimals - FROM - multi_asset AS ma - INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - WHERE - ma.policy = _asset_policy_decoded; + FROM multi_asset AS ma + INNER JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE ma.policy = _asset_policy_decoded; END; $$; diff --git a/files/grest/rpc/blocks/block_txs.sql b/files/grest/rpc/blocks/block_txs.sql index 8bdeeaf6..daf45adf 100644 --- a/files/grest/rpc/blocks/block_txs.sql +++ b/files/grest/rpc/blocks/block_txs.sql @@ -1,7 +1,10 @@ CREATE OR REPLACE FUNCTION grest.block_txs(_block_hashes text []) RETURNS TABLE ( block_hash text, - tx_hashes text [] + tx_hash text, + epoch_no word31type, + block_height word31type, + block_time integer ) LANGUAGE plpgsql AS $$ @@ -21,13 +24,15 @@ BEGIN RETURN QUERY SELECT - encode(b.hash, 'hex'), - ARRAY_AGG(ENCODE(tx.hash::bytea, 'hex')) - FROM - public.block AS b - INNER JOIN public.tx ON tx.block_id = b.id + ENCODE(b.hash, 'hex'), + ENCODE(tx.hash, 'hex') AS tx_hash, + b.epoch_no, + b.block_no AS block_height, + EXTRACT(EPOCH FROM b.time)::integer AS block_time + FROM public.block AS b + INNER JOIN public.tx ON tx.block_id = b.id WHERE b.id = ANY(_block_ids) - GROUP BY b.hash; + ; END; $$; diff --git a/files/grest/rpc/epoch/epoch_info.sql b/files/grest/rpc/epoch/epoch_info.sql index da9e5018..471235fd 100644 --- a/files/grest/rpc/epoch/epoch_info.sql +++ b/files/grest/rpc/epoch/epoch_info.sql @@ -44,9 +44,8 @@ BEGIN eas.amount::text AS active_stake, ei.i_total_rewards::text AS total_rewards, ei.i_avg_blk_reward::text AS avg_blk_reward - FROM - grest.epoch_info_cache AS ei - LEFT JOIN grest.epoch_active_stake_cache AS eas ON eas.epoch_no = ei.epoch_no + FROM grest.epoch_info_cache AS ei + LEFT JOIN grest.epoch_active_stake_cache AS eas ON eas.epoch_no = ei.epoch_no WHERE CASE WHEN _epoch_no IS NULL THEN ei.epoch_no <= (SELECT MAX(epoch.no) FROM public.epoch) @@ -54,7 +53,7 @@ BEGIN ei.epoch_no = _epoch_no END AND - (_include_next_epoch OR ei.i_first_block_time::integer is not null); + (_include_next_epoch OR ei.i_first_block_time::integer IS NOT NULL); END; $$; diff --git a/files/grest/rpc/epoch/epoch_params.sql b/files/grest/rpc/epoch/epoch_params.sql index 9807a468..93534925 100644 --- a/files/grest/rpc/epoch/epoch_params.sql +++ b/files/grest/rpc/epoch/epoch_params.sql @@ -21,7 +21,7 @@ RETURNS TABLE ( min_pool_cost text, nonce text, block_hash text, - cost_models character varying, + cost_models jsonb, price_mem double precision, price_step double precision, max_tx_ex_mem word64type, @@ -36,87 +36,52 @@ RETURNS TABLE ( LANGUAGE plpgsql AS $$ BEGIN - IF _epoch_no IS NULL THEN - RETURN QUERY + RETURN QUERY SELECT - ei.epoch_no, - ei.p_min_fee_a AS min_fee_a, - ei.p_min_fee_b AS min_fee_b, - ei.p_max_block_size AS max_block_size, - ei.p_max_tx_size AS max_tx_size, - ei.p_max_bh_size AS max_bh_size, - ei.p_key_deposit::text AS key_deposit, - ei.p_pool_deposit::text AS pool_deposit, - ei.p_max_epoch AS max_epoch, - ei.p_optimal_pool_count AS optimal_pool_count, - ei.p_influence AS influence, - ei.p_monetary_expand_rate AS monetary_expand_rate, - ei.p_treasury_growth_rate AS treasury_growth_rate, - ei.p_decentralisation AS decentralisation, - ei.p_extra_entropy AS extra_entropy, - ei.p_protocol_major AS protocol_major, - ei.p_protocol_minor AS protocol_minor, - ei.p_min_utxo_value::text AS min_utxo_value, - ei.p_min_pool_cost::text AS min_pool_cost, + ep.epoch_no, + ep.min_fee_a AS min_fee_a, + ep.min_fee_b AS min_fee_b, + ep.max_block_size AS max_block_size, + ep.max_tx_size AS max_tx_size, + ep.max_bh_size AS max_bh_size, + ep.key_deposit::text AS key_deposit, + ep.pool_deposit::text AS pool_deposit, + ep.max_epoch AS max_epoch, + ep.optimal_pool_count AS optimal_pool_count, + ep.influence AS influence, + ep.monetary_expand_rate AS monetary_expand_rate, + ep.treasury_growth_rate AS treasury_growth_rate, + ep.decentralisation AS decentralisation, + ENCODE(ep.extra_entropy, 'hex') AS extra_entropy, + ep.protocol_major AS protocol_major, + ep.protocol_minor AS protocol_minor, + ep.min_utxo_value::text AS min_utxo_value, + ep.min_pool_cost::text AS min_pool_cost, ei.p_nonce AS nonce, ei.p_block_hash AS block_hash, - ei.p_cost_models AS cost_models, - ei.p_price_mem AS price_mem, - ei.p_price_step AS price_step, - ei.p_max_tx_ex_mem AS max_tx_ex_mem, - ei.p_max_tx_ex_steps AS max_tx_ex_steps, - ei.p_max_block_ex_mem AS max_block_ex_mem, - ei.p_max_block_ex_steps AS max_block_ex_steps, - ei.p_max_val_size AS max_val_size, - ei.p_collateral_percent AS collateral_percent, - ei.p_max_collateral_inputs AS max_collateral_inputs, - ei.p_coins_per_utxo_size::text AS coins_per_utxo_size - FROM - grest.epoch_info_cache AS ei - WHERE - ei.epoch_no <= (SELECT MAX(epoch.no) FROM public.epoch) + cm.costs AS cost_models, + ep.price_mem AS price_mem, + ep.price_step AS price_step, + ep.max_tx_ex_mem AS max_tx_ex_mem, + ep.max_tx_ex_steps AS max_tx_ex_steps, + ep.max_block_ex_mem AS max_block_ex_mem, + ep.max_block_ex_steps AS max_block_ex_steps, + ep.max_val_size AS max_val_size, + ep.collateral_percent AS collateral_percent, + ep.max_collateral_inputs AS max_collateral_inputs, + ep.coins_per_utxo_size::text AS coins_per_utxo_size + FROM epoch_param AS ep + LEFT JOIN grest.epoch_info_cache AS ei ON ei.epoch_no = ep.epoch_no + LEFT JOIN cost_model AS cm ON cm.id = ep.cost_model_id + WHERE + CASE + WHEN _epoch_no IS NULL THEN + ep.epoch_no <= (SELECT MAX(epoch.no) FROM public.epoch) + ELSE + ep.epoch_no = _epoch_no + END ORDER BY ei.epoch_no DESC; - ELSE - RETURN QUERY - SELECT - ei.epoch_no, - ei.p_min_fee_a AS min_fee_a, - ei.p_min_fee_b AS min_fee_b, - ei.p_max_block_size AS max_block_size, - ei.p_max_tx_size AS max_tx_size, - ei.p_max_bh_size AS max_bh_size, - ei.p_key_deposit::text AS key_deposit, - ei.p_pool_deposit::text AS pool_deposit, - ei.p_max_epoch AS max_epoch, - ei.p_optimal_pool_count AS optimal_pool_count, - ei.p_influence AS influence, - ei.p_monetary_expand_rate AS monetary_expand_rate, - ei.p_treasury_growth_rate AS treasury_growth_rate, - ei.p_decentralisation AS decentralisation, - ei.p_extra_entropy AS extra_entropy, - ei.p_protocol_major AS protocol_major, - ei.p_protocol_minor AS protocol_minor, - ei.p_min_utxo_value::text AS min_utxo_value, - ei.p_min_pool_cost::text AS min_pool_cost, - ei.p_nonce AS nonce, - ei.p_block_hash AS block_hash, - ei.p_cost_models AS cost_models, - ei.p_price_mem AS price_mem, - ei.p_price_step AS price_step, - ei.p_max_tx_ex_mem AS max_tx_ex_mem, - ei.p_max_tx_ex_steps AS max_tx_ex_steps, - ei.p_max_block_ex_mem AS max_block_ex_mem, - ei.p_max_block_ex_steps AS max_block_ex_steps, - ei.p_max_val_size AS max_val_size, - ei.p_collateral_percent AS collateral_percent, - ei.p_max_collateral_inputs AS max_collateral_inputs, - ei.p_coins_per_utxo_size::text AS coins_per_utxo_size - FROM - grest.epoch_info_cache AS ei - WHERE - ei.epoch_no = _epoch_no; - END IF; END; $$; diff --git a/files/grest/rpc/epoch/epoch_summary_corrections_update.sql b/files/grest/rpc/epoch/epoch_summary_corrections_update.sql new file mode 100644 index 00000000..48b5559c --- /dev/null +++ b/files/grest/rpc/epoch/epoch_summary_corrections_update.sql @@ -0,0 +1,65 @@ +CREATE OR REPLACE function grest.epoch_summary_corrections_update() +RETURNS void +LANGUAGE plpgsql +AS $$ +DECLARE + curr_epoch_record record := null; + latest_epoch bigint = (SELECT MAX(no) FROM epoch); + last_epoch_checked bigint = coalesce((SELECT last_value::bigint FROM grest.control_table WHERE key = 'last_epoch_summary_data_checked'), -1); +BEGIN + RAISE NOTICE 'Last validated epoch was %', last_epoch_checked; + IF last_epoch_checked < 0 THEN + RAISE NOTICE 'Inserting initial record for key last_epoch_summary_data_checked'; + INSERT INTO grest.control_table values('last_epoch_summary_data_checked', 0, null); + END IF; + FOR curr_epoch_record IN ( + SELECT b.epoch_no + FROM + (SELECT + no, + blk_count AS epoch_blk_count, + tx_count AS epoch_tx_count + FROM epoch + WHERE no > last_epoch_checked - 2) AS e, + (SELECT + epoch_no, + COUNT(block_no) AS block_blk_count, + SUM(tx_count) AS block_tx_count + FROM block + WHERE epoch_no > (last_epoch_checked - 2) + GROUP BY epoch_no) AS b + WHERE e.no = b.epoch_no + AND (e.epoch_blk_count != b.block_blk_count OR e.epoch_tx_count != b.block_tx_count) + ORDER BY b.epoch_no + ) LOOP + RAISE NOTICE 'Need to fix up data for epoch %', curr_epoch_record; + WITH agg_table AS + ( SELECT + MIN(block.epoch_no) AS epoch_no, + SUM(tx.out_sum) AS out_sum, + SUM(tx.fee) AS fee_sum, + MIN(block.time) AS start_time, + MAX(block.time) AS end_time, + COUNT(tx.id) AS tx_count, + COUNT(distinct block.block_no) AS blk_count + FROM block + LEFT JOIN tx ON block.id = tx.block_id + WHERE block.epoch_no = curr_epoch_record.epoch_no + ) + + UPDATE epoch + SET + out_sum = COALESCE(agg_table.out_sum, 0), + fees = COALESCE(agg_table.fee_sum, 0), + tx_count = agg_table.tx_count, + blk_count = agg_table.blk_count, + start_time = agg_table.start_time, + end_time = agg_table.end_time + FROM agg_table + WHERE no = agg_table.epoch_no ; + + RAISE NOTICE 'Epoch row for epoch % corrected', curr_epoch_record; + END LOOP; + UPDATE grest.control_table SET last_value = latest_epoch::text WHERE key = 'last_epoch_summary_data_checked'; +END; +$$; diff --git a/files/grest/rpc/pool/pool_delegators.sql b/files/grest/rpc/pool/pool_delegators.sql index df1fe119..e9145a0c 100644 --- a/files/grest/rpc/pool/pool_delegators.sql +++ b/files/grest/rpc/pool/pool_delegators.sql @@ -11,8 +11,8 @@ AS $$ DECLARE _pool_id bigint; BEGIN - RETURN QUERY - WITH + RETURN QUERY + WITH _all_delegations AS ( SELECT sa.id AS stake_address_id, diff --git a/files/grest/rpc/pool/pool_history.sql b/files/grest/rpc/pool/pool_history.sql index 5fe43755..82c3e603 100644 --- a/files/grest/rpc/pool/pool_history.sql +++ b/files/grest/rpc/pool/pool_history.sql @@ -34,8 +34,8 @@ BEGIN COALESCE(member_rewards, 0)::text, COALESCE(epoch_ros, 0) FROM grest.pool_history_cache AS phc - WHERE phc.pool_id = _pool_bech32 and - (_epoch_no IS NULL OR + WHERE phc.pool_id = _pool_bech32 and + (_epoch_no IS NULL OR phc.epoch_no = _epoch_no) ORDER by phc.epoch_no desc; END; diff --git a/files/grest/rpc/pool/pool_info.sql b/files/grest/rpc/pool/pool_info.sql index fea544df..b295c4b0 100644 --- a/files/grest/rpc/pool/pool_info.sql +++ b/files/grest/rpc/pool/pool_info.sql @@ -34,21 +34,20 @@ DECLARE BEGIN SELECT MAX(epoch.no) INTO _epoch_no FROM public.epoch; SELECT FLOOR(supply::bigint / ( - SELECT p_optimal_pool_count - FROM grest.epoch_info_cache - WHERE epoch_no = _epoch_no + SELECT ep.optimal_pool_count + FROM epoch_param AS ep + WHERE ep.epoch_no = _epoch_no ))::bigint INTO _saturation_limit FROM grest.totals(_epoch_no); RETURN QUERY WITH _all_pool_info AS ( SELECT DISTINCT ON (pic.pool_id_bech32) * - FROM - grest.pool_info_cache AS pic - WHERE - pic.pool_id_bech32 = ANY(SELECT UNNEST(_pool_bech32_ids)) + FROM grest.pool_info_cache AS pic + WHERE pic.pool_id_bech32 = ANY(SELECT UNNEST(_pool_bech32_ids)) ORDER BY - pic.pool_id_bech32, pic.tx_id DESC + pic.pool_id_bech32, + pic.tx_id DESC ) SELECT @@ -76,66 +75,40 @@ BEGIN live.stake::text, live.delegators, ROUND((live.stake / _saturation_limit) * 100, 2) - FROM - _all_pool_info AS api + FROM _all_pool_info AS api LEFT JOIN LATERAL ( - ( - SELECT pod.json - FROM - public.pool_offline_data AS pod - WHERE - pod.pool_id = api.pool_hash_id - AND - pod.pmr_id = api.meta_id - ) - UNION ALL - ( - SELECT pod.json - FROM - public.pool_offline_data AS pod - WHERE - pod.pool_id = api.pool_hash_id - AND - pod.json IS NOT NULL - ORDER BY - pod.pmr_id DESC - ) + SELECT pod.json + FROM public.pool_offline_data AS pod + WHERE pod.pool_id = api.pool_hash_id + AND pod.pmr_id = api.meta_id + ORDER BY pod.pmr_id DESC LIMIT 1 - ) offline_data ON TRUE + ) AS offline_data ON TRUE LEFT JOIN LATERAL ( SELECT SUM(COUNT(b.id)) OVER () AS cnt, b.op_cert, b.op_cert_counter - FROM - public.block AS b - INNER JOIN - public.slot_leader AS sl ON b.slot_leader_id = sl.id - WHERE - sl.pool_hash_id = api.pool_hash_id + FROM public.block AS b + INNER JOIN public.slot_leader AS sl ON b.slot_leader_id = sl.id + WHERE sl.pool_hash_id = api.pool_hash_id GROUP BY b.op_cert, b.op_cert_counter - ORDER BY - b.op_cert_counter DESC + ORDER BY b.op_cert_counter DESC LIMIT 1 - ) block_data ON TRUE + ) AS block_data ON TRUE LEFT JOIN LATERAL( SELECT amount::lovelace AS as_sum - FROM - grest.pool_active_stake_cache AS pasc - WHERE - pasc.pool_id = api.pool_id_bech32 - AND - pasc.epoch_no = _epoch_no - ) active_stake ON TRUE + FROM grest.pool_active_stake_cache AS pasc + WHERE pasc.pool_id = api.pool_id_bech32 + AND pasc.epoch_no = _epoch_no + ) AS active_stake ON TRUE LEFT JOIN LATERAL( SELECT amount::lovelace AS es_sum - FROM - grest.epoch_active_stake_cache AS easc - WHERE - easc.epoch_no = _epoch_no - ) epoch_stake ON TRUE + FROM grest.epoch_active_stake_cache AS easc + WHERE easc.epoch_no = _epoch_no + ) AS epoch_stake ON TRUE LEFT JOIN LATERAL( SELECT CASE WHEN api.pool_status = 'retired' @@ -154,11 +127,9 @@ BEGIN ELSE SUM(CASE WHEN sdc.stake_address = ANY(api.owners) THEN total_balance ELSE 0 END)::lovelace END AS pledge - FROM - grest.stake_distribution_cache AS sdc - WHERE - sdc.pool_id = api.pool_id_bech32 - ) live ON TRUE; + FROM grest.stake_distribution_cache AS sdc + WHERE sdc.pool_id = api.pool_id_bech32 + ) AS live ON TRUE; END; $$; diff --git a/files/grest/rpc/pool/pool_list.sql b/files/grest/rpc/pool/pool_list.sql index c656d274..3d8ce843 100644 --- a/files/grest/rpc/pool/pool_list.sql +++ b/files/grest/rpc/pool/pool_list.sql @@ -1,7 +1,19 @@ CREATE OR REPLACE FUNCTION grest.pool_list() RETURNS TABLE ( pool_id_bech32 character varying, - ticker character varying + pool_id_hex text, + active_epoch_no bigint, + margin double precision, + fixed_cost text, + pledge text, + reward_addr character varying, + owners character varying [], + relays jsonb [], + ticker character varying, + meta_url character varying, + meta_hash text, + pool_status text, + retiring_epoch word31type ) LANGUAGE plpgsql AS $$ @@ -12,36 +24,49 @@ BEGIN -- Get last pool update for each pool _pool_list AS ( SELECT - DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, - pool_status - FROM - grest.pool_info_cache AS pic - ORDER BY - pic.pool_id_bech32, - pic.tx_id DESC + ph.view as pool_id_bech32, + ph.hash_raw as pool_id_hex + FROM pool_hash AS ph ), _pool_meta AS ( - SELECT - DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, - pod.ticker_name - FROM - grest.pool_info_cache AS pic - LEFT JOIN public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id - WHERE pod.ticker_name IS NOT NULL + SELECT DISTINCT ON (pic.pool_id_bech32) + pic.pool_id_bech32, + pic.active_epoch_no, + pic.margin, + pic.fixed_cost, + pic.pledge, + pic.reward_addr, + pic.owners, + pic.relays, + pod.ticker_name, + pic.meta_url, + pic.meta_hash, + pic.pool_status, + pic.retiring_epoch + FROM grest.pool_info_cache AS pic + LEFT JOIN public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id ORDER BY pic.pool_id_bech32, pic.tx_id DESC ) - SELECT pl.pool_id_bech32, - pm.ticker_name - FROM - _pool_list AS pl - LEFT JOIN _pool_meta AS pm ON pl.pool_id_bech32 = pm.pool_id_bech32 - WHERE - pool_status != 'retired' + encode(pl.pool_id_hex,'hex') as pool_id_hex, + pm.active_epoch_no, + pm.margin, + pm.fixed_cost::text, + pm.pledge::text, + pm.reward_addr, + pm.owners, + pm.relays, + pm.ticker_name, + pm.meta_url, + pm.meta_hash, + pm.pool_status, + pm.retiring_epoch + FROM _pool_list AS pl + LEFT JOIN _pool_meta AS pm ON pl.pool_id_bech32 = pm.pool_id_bech32 ); END; $$; diff --git a/files/grest/rpc/pool/pool_metadata.sql b/files/grest/rpc/pool/pool_metadata.sql index 872baf18..c919791c 100644 --- a/files/grest/rpc/pool/pool_metadata.sql +++ b/files/grest/rpc/pool/pool_metadata.sql @@ -3,32 +3,32 @@ RETURNS TABLE ( pool_id_bech32 character varying, meta_url character varying, meta_hash text, - meta_json jsonb + meta_json jsonb, + pool_status text ) LANGUAGE plpgsql AS $$ #variable_conflict use_column BEGIN RETURN QUERY - SELECT - DISTINCT ON (pic.pool_id_bech32) pool_id_bech32, + SELECT DISTINCT ON (pic.pool_id_bech32) + ph.view AS pool_id_bech32, pic.meta_url, pic.meta_hash, - pod.json - FROM - grest.pool_info_cache AS pic - LEFT JOIN - public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id + pod.json, + pic.pool_status + FROM public.pool_hash AS ph + LEFT JOIN grest.pool_info_cache AS pic ON ph.view = pic.pool_id_bech32 + LEFT JOIN public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id WHERE - pic.pool_status != 'retired' - AND CASE WHEN _pool_bech32_ids IS NULL THEN TRUE WHEN _pool_bech32_ids IS NOT NULL THEN pic.pool_id_bech32 = ANY(SELECT UNNEST(_pool_bech32_ids)) END ORDER BY - pic.pool_id_bech32, pic.tx_id DESC; + pic.pool_id_bech32, + pic.tx_id DESC; END; $$; -COMMENT ON FUNCTION grest.pool_metadata IS 'Metadata(on & off-chain) for all currently registered/retiring (not retired) pools'; -- noqa: LT01 +COMMENT ON FUNCTION grest.pool_metadata IS 'Metadata(on & off-chain) for all pools'; -- noqa: LT01 diff --git a/files/grest/rpc/pool/pool_registrations.sql b/files/grest/rpc/pool/pool_registrations.sql new file mode 100644 index 00000000..855ff4bd --- /dev/null +++ b/files/grest/rpc/pool/pool_registrations.sql @@ -0,0 +1,28 @@ +CREATE OR REPLACE FUNCTION grest.pool_registrations(_epoch_no numeric) +RETURNS TABLE ( + pool_id_bech32 text, + tx_hash text, + block_hash text, + block_height word31type, + epoch_no word31type, + epoch_slot word31type, + active_epoch_no word31type +) +LANGUAGE SQL STABLE +AS $$ + SELECT + ph.view, + ENCODE(tx.hash,'hex'), + ENCODE(b.hash,'hex'), + b.block_no, + b.epoch_no, + b.epoch_slot_no, + pu.active_epoch_no + FROM pool_update AS pu + LEFT JOIN tx ON pu.registered_tx_id = tx.id + INNER JOIN block AS b ON tx.block_id = b.id + LEFT JOIN pool_hash AS ph ON ph.id = pu.hash_id + WHERE b.epoch_no = _epoch_no; +$$; + +COMMENT ON FUNCTION grest.pool_registrations IS 'A list of all pool registrations initiated in the requested epoch'; --noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/pool/pool_relays.sql b/files/grest/rpc/pool/pool_relays.sql index 29fd8c28..84b70ab9 100644 --- a/files/grest/rpc/pool/pool_relays.sql +++ b/files/grest/rpc/pool/pool_relays.sql @@ -1,23 +1,23 @@ CREATE OR REPLACE FUNCTION grest.pool_relays() RETURNS TABLE ( pool_id_bech32 character varying, - relays jsonb [] + relays jsonb [], + pool_status text ) LANGUAGE plpgsql AS $$ #variable_conflict use_column BEGIN RETURN QUERY - SELECT - DISTINCT ON (pool_id_bech32) pool_id_bech32, - relays - FROM - grest.pool_info_cache - WHERE - pool_status != 'retired' + SELECT DISTINCT ON (pool_id_bech32) + pool_id_bech32, + relays, + pool_status + FROM grest.pool_info_cache ORDER BY - pool_id_bech32, tx_id DESC; + pool_id_bech32, + tx_id DESC; END; $$; -COMMENT ON FUNCTION grest.pool_relays IS 'A list of registered relays for all currently registered/retiring (not retired) pools'; --noqa: LT01 +COMMENT ON FUNCTION grest.pool_relays IS 'A list of registered relays for all pools'; --noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/pool/pool_retirements.sql b/files/grest/rpc/pool/pool_retirements.sql new file mode 100644 index 00000000..d2e7a16a --- /dev/null +++ b/files/grest/rpc/pool/pool_retirements.sql @@ -0,0 +1,28 @@ +CREATE OR REPLACE FUNCTION grest.pool_retirements(_epoch_no numeric) +RETURNS TABLE ( + pool_id_bech32 text, + tx_hash text, + block_hash text, + block_height word31type, + epoch_no word31type, + epoch_slot word31type, + active_epoch_no word31type +) +LANGUAGE SQL STABLE +AS $$ + SELECT + ph.view, + ENCODE(tx.hash,'hex'), + ENCODE(b.hash,'hex'), + b.block_no, + b.epoch_no, + b.epoch_slot_no, + pr.retiring_epoch + FROM pool_retire AS pr + LEFT JOIN tx ON pr.announced_tx_id = tx.id + INNER JOIN block AS b ON tx.block_id = b.id + LEFT JOIN pool_hash AS ph ON ph.id = pr.hash_id + WHERE b.epoch_no = _epoch_no; +$$; + +COMMENT ON FUNCTION grest.pool_registrations IS 'A list of all pool retirements initiated in the requested epoch'; --noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/pool/pool_updates.sql b/files/grest/rpc/pool/pool_updates.sql index ac4ca722..4cd4c48d 100644 --- a/files/grest/rpc/pool/pool_updates.sql +++ b/files/grest/rpc/pool/pool_updates.sql @@ -15,42 +15,77 @@ RETURNS TABLE ( meta_url character varying, meta_hash text, meta_json jsonb, - pool_status text, + update_type text, retiring_epoch word31type ) LANGUAGE plpgsql AS $$ -#variable_conflict use_column +DECLARE + _current_epoch_no word31type; BEGIN + SELECT COALESCE(MAX(no), 0) INTO _current_epoch_no FROM public.epoch; RETURN QUERY - SELECT - tx_hash, - block_time::integer, - pool_id_bech32, - pool_id_hex, - active_epoch_no, - vrf_key_hash, - margin, - fixed_cost::text, - pledge::text, - reward_addr, - owners, - relays, - meta_url, - meta_hash, - pod.json, - pool_status, - retiring_epoch - FROM - grest.pool_info_cache AS pic - LEFT JOIN public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id - WHERE - _pool_bech32 IS NULL - OR - pool_id_bech32 = _pool_bech32 + WITH + pool_reg AS ( + SELECT + pic.tx_hash, + pic.block_time::integer, + pic.pool_id_bech32, + pic.pool_id_hex, + pic.active_epoch_no, + pic.vrf_key_hash, + pic.margin, + pic.fixed_cost::text, + pic.pledge::text, + pic.reward_addr, + pic.owners, + pic.relays, + pic.meta_url, + pic.meta_hash, + pod.json, + 'registration' AS update_type, + NULL::word31type AS retiring_epoch + FROM + grest.pool_info_cache AS pic + LEFT JOIN public.pool_offline_data AS pod ON pod.pmr_id = pic.meta_id + LEFT JOIN public.pool_retire AS pr ON pic.pool_hash_id = pr.hash_id + WHERE _pool_bech32 IS NULL + OR pic.pool_id_bech32 = _pool_bech32), + pool_dereg AS ( + SELECT + ENCODE(tx.hash::bytea, 'hex') AS tx_hash, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + ph.view AS pool_id_bech32, + ENCODE(ph.hash_raw::bytea, 'hex') AS pool_id_hex, + NULL::bigint AS active_epoch_no, + NULL AS vrf_key_hash, + NULL::bigint AS margin, + NULL as fixed_cost, + NULL AS pledge, + NULL AS reward_addr, + NULL::text[] AS owners, + NULL::jsonb[] AS relays, + NULL AS meta_url, + NULL AS meta_hash, + NULL::jsonb AS json, + CASE + WHEN pr.retiring_epoch IS NULL THEN 'registration' + ELSE 'deregistration' + END AS update_type, + pr.retiring_epoch::word31type + FROM public.pool_hash AS ph + LEFT JOIN pool_retire AS pr ON pr.hash_id = ph.id + INNER JOIN public.tx ON tx.id = pr.announced_tx_id + INNER JOIN public.block AS b ON b.id = tx.block_id + WHERE _pool_bech32 IS NULL + OR ph.view = _pool_bech32) + SELECT * FROM pool_reg + UNION SELECT * FROM pool_dereg ORDER BY - tx_id DESC; + block_time DESC; END; $$; COMMENT ON FUNCTION grest.pool_updates IS 'Return all pool_updates for all pools or only updates for specific pool if specified'; -- noqa: LT01 + +SELECT grest.pool_updates(); \ No newline at end of file diff --git a/files/grest/rpc/script/datum_info.sql b/files/grest/rpc/script/datum_info.sql index 663a1913..c09c7d81 100644 --- a/files/grest/rpc/script/datum_info.sql +++ b/files/grest/rpc/script/datum_info.sql @@ -1,6 +1,7 @@ CREATE OR REPLACE FUNCTION grest.datum_info(_datum_hashes text []) RETURNS TABLE ( - hash text, + datum_hash text, + creation_tx_hash text, value jsonb, bytes text ) @@ -13,14 +14,14 @@ BEGIN FROM UNNEST(_datum_hashes) AS d_hash; RETURN QUERY SELECT - ENCODE(d.hash, 'hex'), + ENCODE(d.hash,'hex'), + ENCODE(tx.hash,'hex') AS creation_tx_hash, d.value, - ENCODE(d.bytes, 'hex') - FROM - datum AS d - WHERE - d.hash = ANY(_datum_hashes_decoded); + ENCODE(d.bytes,'hex') + FROM datum AS d + INNER JOIN tx ON tx.id = d.tx_id + WHERE d.hash = ANY(_datum_hashes_decoded); END; $$; -COMMENT ON FUNCTION grest.datum_info IS 'Get information about a given data FROM hashes.'; -- noqa: LT01 +COMMENT ON FUNCTION grest.datum_info IS 'Get information about a given datum FROM hashes.'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/script/native_script_list.sql b/files/grest/rpc/script/native_script_list.sql index a0af9728..7f0482e1 100644 --- a/files/grest/rpc/script/native_script_list.sql +++ b/files/grest/rpc/script/native_script_list.sql @@ -2,22 +2,22 @@ CREATE OR REPLACE FUNCTION grest.native_script_list() RETURNS TABLE ( script_hash text, creation_tx_hash text, - type scripttype, - script jsonb + type text, + size word31type ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT - ENCODE(script.hash, 'hex'), - ENCODE(tx.hash, 'hex'), - script.type, - script.json - FROM script - INNER JOIN tx ON tx.id = script.tx_id - WHERE script.type IN ('timelock', 'multisig'); + ENCODE(s.hash, 'hex')::text AS script_hash, + ENCODE(tx.hash, 'hex')::text AS creation_tx_hash, + s.type::text AS type, + s.serialised_size AS size + FROM script AS s + INNER JOIN tx ON tx.id = s.tx_id + WHERE s.type IN ('timelock', 'multisig'); END; $$; -COMMENT ON FUNCTION grest.native_script_list IS 'Get a list of all native(multisig/timelock) script hashes with creation tx hash, type and script in json format.'; --noqa: LT01 +COMMENT ON FUNCTION grest.native_script_list IS 'Get a list of all native(multisig/timelock) script hashes with creation tx hash, type and script size.'; --noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/script/plutus_script_list.sql b/files/grest/rpc/script/plutus_script_list.sql index e149f5d8..b7f66b56 100644 --- a/files/grest/rpc/script/plutus_script_list.sql +++ b/files/grest/rpc/script/plutus_script_list.sql @@ -1,19 +1,23 @@ CREATE OR REPLACE FUNCTION grest.plutus_script_list() RETURNS TABLE ( script_hash text, - creation_tx_hash text + creation_tx_hash text, + type text, + size word31type ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT - ENCODE(script.hash, 'hex') AS script_hash, - ENCODE(tx.hash, 'hex') AS creation_tx_hash - FROM script - INNER JOIN tx ON tx.id = script.tx_id - WHERE script.type IN ('plutusV1', 'plutusV2'); + ENCODE(s.hash,'hex')::text AS script_hash, + ENCODE(tx.hash,'hex')::text AS creation_tx_hash, + s.type::text AS type, + s.serialised_size AS size + FROM script AS s + INNER JOIN tx ON tx.id = s.tx_id + WHERE s.type IN ('plutusV1', 'plutusV2'); END; $$; -COMMENT ON FUNCTION grest.plutus_script_list IS 'Get a list of all plutus script hashes with creation tx hash.'; --noqa: LT01 +COMMENT ON FUNCTION grest.plutus_script_list IS 'Get a list of all plutus script hashes with creation tx hash.'; --noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/script/script_info.sql b/files/grest/rpc/script/script_info.sql new file mode 100644 index 00000000..c7322d4b --- /dev/null +++ b/files/grest/rpc/script/script_info.sql @@ -0,0 +1,32 @@ +CREATE OR REPLACE FUNCTION grest.script_info(_script_hashes text []) +RETURNS TABLE ( + script_hash text, + creation_tx_hash text, + type text, + value jsonb, + bytes text, + size word31type +) +LANGUAGE plpgsql +AS $$ +DECLARE + _script_hashes_decoded bytea[]; +BEGIN + SELECT INTO _script_hashes_decoded ARRAY_AGG(DECODE(s_hash, 'hex')) + FROM UNNEST(_script_hashes) AS s_hash; + RETURN QUERY + SELECT + ENCODE(s.hash,'hex') AS script_hash, + ENCODE(tx.hash,'hex') AS creation_tx_hash, + s.type::text AS type, + s.json AS value, + ENCODE(s.bytes,'hex')::text AS bytes, + s.serialised_size AS size + FROM script AS s + INNER JOIN tx ON tx.id = s.tx_id + WHERE s.hash = ANY(_script_hashes_decoded) + ; +END; +$$; + +COMMENT ON FUNCTION grest.script_info IS 'Get information about a given script FROM hashes.'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/script/script_utxos.sql b/files/grest/rpc/script/script_utxos.sql new file mode 100644 index 00000000..65ba9890 --- /dev/null +++ b/files/grest/rpc/script/script_utxos.sql @@ -0,0 +1,81 @@ +CREATE OR REPLACE FUNCTION grest.script_utxos(_script_hash text, _extended boolean DEFAULT false) +RETURNS TABLE ( + tx_hash text, + tx_index smallint, + address text, + value text, + stake_address text, + payment_cred text, + epoch_no word31type, + block_height word31type, + block_time integer, + datum_hash text, + inline_datum jsonb, + reference_script jsonb, + asset_list jsonb, + is_spent boolean +) +LANGUAGE plpgsql +AS $$ +DECLARE + known_addresses varchar[]; +BEGIN + RETURN QUERY + SELECT + ENCODE(tx.hash, 'hex')::text AS tx_hash, + tx_out.index::smallint, + tx_out.address::text, + tx_out.value::text, + sa.view::text as stake_address, + ENCODE(tx_out.payment_cred, 'hex') AS payment_cred, + b.epoch_no, + b.block_no, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE + WHEN _extended = false OR tx_out.inline_datum_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END) AS inline_datum, + (CASE + WHEN _extended = false OR tx_out.reference_script_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END) AS reference_script, + (CASE + WHEN _extended = false OR ma.policy IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, + 'decimals', aic.decimals, + 'quantity', mto.quantity::text + ) + END + ) AS asset_list, + (CASE + WHEN tx_out.consumed_by_tx_in_id IS NULL THEN false + ELSE true + END) AS is_spent + FROM tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + INNER JOIN script ON script.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN block AS b ON b.id = tx.block_id + LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + WHERE script.hash = DECODE(_script_hash,'hex') + ; +END; +$$; + +COMMENT ON FUNCTION grest.script_utxos IS 'Get UTxO details for requested scripts'; -- noqa: LT01 \ No newline at end of file diff --git a/files/grest/rpc/transactions/tx_info.sql b/files/grest/rpc/transactions/tx_info.sql index 7719947c..44b3c78c 100644 --- a/files/grest/rpc/transactions/tx_info.sql +++ b/files/grest/rpc/transactions/tx_info.sql @@ -663,9 +663,9 @@ BEGIN ind.hash AS ind_hash, ind.value AS ind_value FROM redeemer - INNER JOIN tx_in ON tx_in.redeemer_id = redeemer.id - INNER JOIN tx_out AS inutxo ON inutxo.tx_id = tx_in.tx_out_id AND inutxo.index = tx_in.tx_out_index - INNER JOIN datum AS ind ON ind.hash = inutxo.data_hash + INNER JOIN tx_in ON tx_in.redeemer_id = redeemer.id + INNER JOIN tx_out AS inutxo ON inutxo.tx_id = tx_in.tx_out_id AND inutxo.index = tx_in.tx_out_index + INNER JOIN datum AS ind ON ind.hash = inutxo.data_hash WHERE redeemer.tx_id = ANY(_tx_id_list) ) @@ -845,7 +845,7 @@ BEGIN COALESCE((SELECT ans.list FROM _all_native_scripts AS ans WHERE ans.tx_id = atx.id), JSONB_BUILD_ARRAY()), COALESCE((SELECT apc.list FROM _all_plutus_contracts AS apc WHERE apc.tx_id = atx.id), JSONB_BUILD_ARRAY()) FROM _all_tx AS atx - WHERE atx.tx_hash = ANY(_tx_hashes_bytea) + WHERE atx.id = ANY(_tx_id_list) ); END; diff --git a/files/grest/rpc/transactions/tx_utxos.sql b/files/grest/rpc/transactions/tx_utxos.sql index af477a70..bda74b2e 100644 --- a/files/grest/rpc/transactions/tx_utxos.sql +++ b/files/grest/rpc/transactions/tx_utxos.sql @@ -66,7 +66,7 @@ BEGIN LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id LEFT JOIN multi_asset AS ma ON ma.id = mto.ident LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - WHERE + WHERE tx_in.tx_in_id = ANY(_tx_id_list) ), @@ -97,12 +97,12 @@ BEGIN LEFT JOIN ma_tx_out AS mto ON mto.tx_out_id = tx_out.id LEFT JOIN multi_asset AS ma ON ma.id = mto.ident LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - WHERE + WHERE tx_out.tx_id = ANY(_tx_id_list) ) SELECT - ENCODE(atx.tx_hash, 'hex'), + ENCODE(atx.tx_hash, 'hex'), COALESCE(( SELECT JSONB_AGG(tx_inputs) FROM ( diff --git a/files/grest/rpc/transactions/utxo_info.sql b/files/grest/rpc/transactions/utxo_info.sql new file mode 100644 index 00000000..22b9fb7f --- /dev/null +++ b/files/grest/rpc/transactions/utxo_info.sql @@ -0,0 +1,113 @@ +CREATE OR REPLACE FUNCTION grest.utxo_info(_utxo_refs text [], _extended boolean DEFAULT false) +RETURNS TABLE ( + tx_hash text, + tx_index smallint, + address text, + value text, + stake_address text, + payment_cred text, + epoch_no word31type, + block_height word31type, + block_time integer, + datum_hash text, + inline_datum jsonb, + reference_script jsonb, + asset_list jsonb, + is_spent boolean +) +LANGUAGE plpgsql +AS $$ +DECLARE + _tx_hashes_bytea bytea[]; + _tx_id_list bigint[]; +BEGIN + -- convert input _tx_hashes array into bytea array + DROP TABLE IF EXISTS utxo_refs; + CREATE TEMP TABLE utxo_refs AS ( + SELECT + DECODE(SPLIT_PART(ur,'#',1), 'hex') AS tx_hashes, + SPLIT_PART(ur,'#',2) AS tx_index + FROM UNNEST(_utxo_refs) AS ur + ); + + -- all tx ids + SELECT INTO _tx_id_list ARRAY_AGG(id) + FROM ( + SELECT txo.id + FROM tx_out AS txo + INNER JOIN tx ON tx.id = txo.tx_id + INNER JOIN utxo_refs AS ur ON ur.tx_hashes = tx.hash + AND ur.tx_index::smallint = txo.index + ) AS tmp; + + RETURN QUERY + WITH + _assets AS ( + SELECT + txo.id, + JSONB_AGG(CASE WHEN ma.policy IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'policy_id', ENCODE(ma.policy, 'hex'), + 'asset_name', ENCODE(ma.name, 'hex'), + 'fingerprint', ma.fingerprint, + 'decimals', aic.decimals, + 'quantity', mto.quantity::text + ) + END) as assets + FROM tx_out AS txo + INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txo.id + LEFT JOIN multi_asset AS ma ON ma.id = mto.ident + LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id + WHERE txo.id = ANY(_tx_id_list) + GROUP BY txo.id + ) + SELECT + ENCODE(tx.hash, 'hex')::text AS tx_hash, + tx_out.index::smallint, + tx_out.address::text, + tx_out.value::text, + sa.view::text as stake_address, + ENCODE(tx_out.payment_cred, 'hex') AS payment_cred, + b.epoch_no, + b.block_no, + EXTRACT(EPOCH FROM b.time)::integer AS block_time, + ENCODE(tx_out.data_hash, 'hex') AS datum_hash, + (CASE + WHEN _extended = false OR tx_out.inline_datum_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'bytes', ENCODE(datum.bytes, 'hex'), + 'value', datum.value + ) + END) AS inline_datum, + (CASE + WHEN _extended = false OR tx_out.reference_script_id IS NULL THEN NULL + ELSE JSONB_BUILD_OBJECT( + 'hash', ENCODE(script.hash, 'hex'), + 'bytes', ENCODE(script.bytes, 'hex'), + 'value', script.json, + 'type', script.type::text, + 'size', script.serialised_size + ) + END) AS reference_script, + CASE + WHEN _extended = false THEN NULL + ELSE COALESCE(assets, JSONB_BUILD_ARRAY()) + END AS asset_list, + (CASE + WHEN tx_out.consumed_by_tx_in_id IS NULL THEN false + ELSE true + END) AS is_spent + FROM tx_out + INNER JOIN tx ON tx_out.tx_id = tx.id + LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id + LEFT JOIN block AS b ON b.id = tx.block_id + LEFT JOIN datum ON datum.id = tx_out.inline_datum_id + LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN _assets ON tx_out.id = _assets.id + WHERE tx_out.id = ANY(_tx_id_list) + ; + +END; +$$; + +COMMENT ON FUNCTION grest.utxo_info IS 'Get details for requested UTxO arrays (UTxOs accepted in # format).'; -- noqa: LT01 \ No newline at end of file diff --git a/html/index.html b/html/index.html index cd6e9a17..05404915 100644 --- a/html/index.html +++ b/html/index.html @@ -22,7 +22,7 @@

    diff --git a/specs/results/koiosapi-guild.yaml b/specs/results/koiosapi-guild.yaml index 2027146d..1bfb461d 100644 --- a/specs/results/koiosapi-guild.yaml +++ b/specs/results/koiosapi-guild.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.10 + version: v1.1.0rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. @@ -14,11 +14,11 @@ info: Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

    ``` bash - curl "https://api.koios.rest/api/v0/tip" + curl "https://api.koios.rest/api/v1/tip" # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] - curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" + curl "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_height" # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] ``` @@ -27,7 +27,7 @@ info: You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

    ``` bash - curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" + curl "https://api.koios.rest/api/v1/blocks?epoch=eq.250&epoch_slot=lt.180" # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] @@ -62,7 +62,7 @@ info: Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -I | grep -i content-range # content-range: 0-999/* @@ -71,7 +71,7 @@ info: As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range # content-range: 1000-1499/* @@ -80,7 +80,7 @@ info: For GET endpoints, there is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range # content-range: 1000-1499/* @@ -94,7 +94,7 @@ info: Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" + curl -s "https://api.koios.rest/api/v1/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, @@ -109,13 +109,13 @@ info: Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" # epoch,epoch_slot,block_time # 318,28491,1643607582 @@ -134,6 +134,10 @@ info: Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. + # Authentication + + While Koios public tier remains unauthenticated and allows queries without any authentication, it has low limits to prevent actions against an erroraneous query/loop from a consumer. There is also a Free tier which requires setting up Bearer Auth token that is linked to the owner's wallet account (which can be connected to via [Koios website](https://koios.rest/pricing/Pricing.html) ). + The examples across this API site already [supports authentication](/#auth), for you to use in the queries. # Community projects @@ -142,11 +146,12 @@ info: x-logo: url: "https://api.koios.rest/images/koios.png" servers: - - url: https://api.koios.rest/api/v0 - - url: https://guild.koios.rest/api/v0 - - url: https://preview.koios.rest/api/v0 - - url: https://preprod.koios.rest/api/v0 + - url: https://api.koios.rest/api/v1 + - url: https://guild.koios.rest/api/v1 + - url: https://preview.koios.rest/api/v1 + - url: https://preprod.koios.rest/api/v1 paths: + /tip: #RPC get: tags: @@ -227,6 +232,45 @@ paths: $ref: "#/components/responses/NotFound" summary: Param Update Proposals description: Get all parameter update proposals submitted to the chain starting Shelley era + /reserve_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from reserves against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Reserve Withdrawals + description: List of all withdrawals from reserves against stake accounts + /treasury_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from treasury against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Treasury Withdrawals + description: List of all withdrawals from treasury against stake accounts + /epoch_info: #RPC get: tags: @@ -294,6 +338,7 @@ paths: summary: Epoch's Block Protocols description: >- Get the information about block protocol distribution in epoch + /blocks: get: tags: @@ -355,28 +400,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Block Transactions description: Get a list of all transactions included in provided blocks - /tx_info: #RPC + + /utxo_info: #RPC post: tags: - Transactions requestBody: - $ref: "#/components/requestBodies/tx_ids" + $ref: "#/components/requestBodies/utxo_refs_with_extended" responses: "200": - description: Array of detailed information about transaction(s) + description: Array of UTXO details content: application/json: schema: - $ref: "#/components/schemas/tx_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Information - description: Get detailed information about transaction(s) - /tx_utxos: #RPC + summary: UTxO Info + description: Get UTxO set for requested UTxO references + /tx_info: #RPC post: tags: - Transactions @@ -384,19 +430,19 @@ paths: $ref: "#/components/requestBodies/tx_ids" responses: "200": - description: Array of inputs and outputs for given transaction(s) + description: Array of detailed information about transaction(s) content: application/json: schema: - $ref: "#/components/schemas/tx_utxos" + $ref: "#/components/schemas/tx_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction UTxOs [DEPRECATED] - description: Get UTxO set (inputs/outputs) of transactions. + summary: Transaction Information + description: Get detailed information about transaction(s) /tx_metadata: #RPC post: tags: @@ -450,7 +496,7 @@ paths: # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. curl -X POST \ --header "Content-Type: application/cbor" \ - --data-binary ${data} https://api.koios.rest/api/v0/submittx + --data-binary @${data} https://api.koios.rest/api/v1/submittx responses: "202": description: OK @@ -486,8 +532,31 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Status (Block Confirmations) + summary: Transaction Status description: Get the number of block confirmations for a given transaction hash list + /tx_utxos: #RPC + post: + tags: + - Transactions + deprecated: true + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of inputs and outputs for given transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_utxos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction UTxOs + description: Get UTxO set (inputs/outputs) of transactions [DEPRECATED - Use /utxo_info instead]. + /address_info: #RPC post: tags: @@ -509,27 +578,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Address Information description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses - /address_txs: #RPC + /address_utxos: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/address_txs" + $ref: "#/components/requestBodies/payment_addresses_with_extended" responses: "200": - description: Array of transaction hashes + description: Array of address UTXOs content: application/json: schema: - $ref: "#/components/schemas/address_txs" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Transactions - description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) + summary: Address UTXOs + description: Get UTxO set for given addresses /credential_utxos: #RPC post: tags: @@ -542,7 +611,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_utxos" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": @@ -550,28 +619,28 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: UTxOs from payment credentials - description: Get a list of UTxO against input payment credential array including their balances - /address_assets: #RPC + description: Get UTxO details for requested payment credentials + /address_txs: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/payment_addresses" + $ref: "#/components/requestBodies/address_txs" responses: "200": - description: Array of address-owned assets + description: Array of transaction hashes content: application/json: schema: - $ref: "#/components/schemas/address_assets" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for given addresses + summary: Address Transactions + description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /credential_txs: #RPC post: tags: @@ -584,7 +653,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_txs" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": @@ -593,6 +662,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Transactions from payment credentials description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) + /address_assets: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address-owned assets + content: + application/json: + schema: + $ref: "#/components/schemas/address_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Assets + description: Get the list of all the assets (policy, name and quantity) for given addresses + /account_list: get: tags: @@ -633,48 +724,70 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses - /account_utxos: #RPC - get: + /account_info_cached: #RPC + post: tags: - Stake Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses" responses: "200": - description: Array of account UTxOs associated with stake address + description: Array of account information content: application/json: schema: - $ref: "#/components/schemas/account_utxos" + $ref: "#/components/schemas/account_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account UTxOs - description: Get a list of all UTxOs for a given stake address (account) - /account_info_cached: #RPC + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (effective for performance query against registered accounts) + /account_utxos: #RPC post: tags: - Stake Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_extended" responses: "200": - description: Array of account information + description: Array of account UTxOs associated with given stake addresses content: application/json: schema: - $ref: "#/components/schemas/account_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account Information (Cached) - description: Get the cached account information for given stake addresses, effective for registered accounts + summary: UTxOs for stake addresses (accounts) + description: Get a list of all UTxOs for given stake addresses (account)s + /account_txs: #RPC + get: + tags: + - Stake Account + parameters: + - $ref: "#/components/parameters/_stake_address" + - $ref: "#/components/parameters/_after_block_height" + responses: + "200": + description: Array of Txs associated with stake address (account) + content: + application/json: + schema: + $ref: "#/components/schemas/address_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Txs + description: Get a list of all Txs for a given stake address (account) /account_rewards: #RPC post: tags: @@ -783,6 +896,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Account History description: Get the staking history of given stake addresses (accounts) + /asset_list: get: tags: @@ -802,95 +916,89 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) - /asset_token_registry: + /policy_asset_list: #RPC get: tags: - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" responses: "200": - description: Array of token registry information for each asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_token_registry" + $ref: "#/components/schemas/policy_asset_list" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Token Registry - description: Get a list of assets registered via token registry on github - /asset_addresses: #RPC + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) + /asset_token_registry: get: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of token registry information for each asset content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_token_registry" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Addresses - description: Get the list of all addresses holding a given asset

    - `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects - with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to - query layers to have a dedicated cache table for themselves served via Koios.` - /asset_address_list: #RPC - get: + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github + /asset_info: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + requestBody: + $ref: "#/components/requestBodies/asset_list" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of detailed asset information content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Address List [DEPRECATED] - description: Get the list of all addresses holding a given asset (replaced by asset_addresses) - /asset_nft_address: #RPC - get: + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata + /asset_utxos: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy_nft" - - $ref: "#/components/parameters/_asset_name_nft" + requestBody: + $ref: "#/components/requestBodies/asset_list_with_extended" responses: "200": - description: Payment addresses currently holding the given NFT + description: Array of UTXOs for given asset list content: application/json: schema: - $ref: "#/components/schemas/asset_nft_address" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: NFT Address - description: Get the address where specified NFT currently reside on. - /asset_info: #RPC + summary: Asset UTXOs + description: Get the UTXO information of a list of assets including + /asset_history: #RPC get: tags: - Asset @@ -899,61 +1007,66 @@ paths: - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of asset mint/burn history content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_history" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information - description: Get the information of an asset including first minting & token registry metadata - post: + summary: Asset History + description: Get the mint/burn history of an asset + /asset_addresses: #RPC + get: tags: - Asset - requestBody: - $ref: "#/components/requestBodies/asset_list" + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information (Bulk) - description: Get the information of a list of assets including first minting & token registry metadata - /asset_history: #RPC + summary: Asset Addresses + description: Get the list of all addresses holding a given asset

    + `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects + with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to + query layers to have a dedicated cache table for themselves served via Koios.` + /asset_nft_address: #RPC get: tags: - Asset parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" responses: "200": - description: Array of asset mint/burn history + description: Payment addresses currently holding the given NFT content: application/json: schema: - $ref: "#/components/schemas/asset_history" + $ref: "#/components/schemas/asset_nft_address" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset History - description: Get the mint/burn history of an asset + summary: NFT Address + description: Get the address where specified NFT currently reside on. /policy_asset_addresses: #RPC get: tags: @@ -999,94 +1112,98 @@ paths: $ref: "#/components/responses/NotFound" summary: Policy Asset Information description: Get the information for all assets under the same policy - /asset_policy_info: #RPC + /asset_summary: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed information of assets under the same policy + description: Array of asset summary information content: application/json: schema: - $ref: "#/components/schemas/policy_asset_info" + $ref: "#/components/schemas/asset_summary" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information [DEPRECATED] - description: Get the information for all assets under the same policy (replaced by asset_addresses) - /policy_asset_list: #RPC + summary: Asset Summary + description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) + /asset_txs: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": - description: Array of detailed information of assets under the same policy + description: An array of Tx hashes that included the given asset (latest first) content: application/json: schema: - $ref: "#/components/schemas/policy_asset_list" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Policy Asset List - description: Get the list of asset under the given policy (including balances) - /asset_summary: #RPC + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) + /asset_address_list: #RPC get: tags: - Asset + deprecated: true parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of asset summary information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_summary" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Summary - description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) - /asset_txs: #RPC + summary: Asset Address List + description: Get the list of all addresses holding a given asset [DEPRECATED - replaced by asset_addresses] + /asset_policy_info: #RPC get: + deprecated: true tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - - $ref: "#/components/parameters/_after_block_height" - - $ref: "#/components/parameters/_history" responses: "200": - description: Array of Tx hashes that included the given asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_txs" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transactions - description: Get the list of current or all asset transaction hashes (newest first) + summary: Asset Policy Information + description: Get the information for all assets under the same policy (DEPRECATED - replaced by policy_asset_info) + /pool_list: #RPC get: tags: @@ -1105,7 +1222,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool List - description: A list of all currently registered/retiring (not retired) pools + description: List of brief info for all pools /pool_info: #RPC post: tags: @@ -1261,6 +1378,48 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Updates (History) description: Return all pool updates for all pools or only updates for specific pool if specified + /pool_registrations: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Registrations + description: Return all pool registrations initiated in the requested epoch + /pool_retirements: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Retirements + description: Return all pool retirements initiated in the requested epoch /pool_relays: #RPC get: tags: @@ -1279,7 +1438,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Relays - description: A list of registered relays for all currently registered/retiring (not retired) pools + description: A list of registered relays for all pools /pool_metadata: #RPC post: tags: @@ -1300,7 +1459,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Metadata - description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools + description: Metadata (on & off-chain) for all pools + + /script_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/script_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/script_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes /native_script_list: #RPC get: tags: @@ -1311,7 +1492,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/native_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1330,7 +1511,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/plutus_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1360,6 +1541,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /script_utxos: #RPC + get: + tags: + - Script + parameters: + - $ref: "#/components/parameters/_script_hash" + - $ref: "#/components/parameters/_extended" + responses: + "200": + description: List of UTXOs for a given script hash + content: + application/json: + schema: + $ref: "#/components/schemas/utxo_infos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Script UTXOs + description: List of all UTXOs for a given script hash /datum_info: #RPC post: tags: @@ -1381,6 +1584,43 @@ paths: $ref: "#/components/responses/NotFound" summary: Datum Information description: List of datum information for given datum hashes + /ogmios/?EvaluateTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains EvaluateTransaction payload as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?EvaluateTransaction + responses: + "200": + description: OK + "400": + description: An error occured while submitting transaction. + summary: Evaluate Transaction + description: Evaluate execution units of scripts in a well-formed transaction. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?EvaluateTransaction) for complete spec + /ogmios/?SubmitTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains a CBOR-serialized signed transaction (base16-encoded) as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?SubmitTransaction + responses: + "200": + description: OK + "400": + description: An error occured while querying transaction. + summary: Submit Transaction + description: Submit a signed and serialized transaction to the network. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?SubmitTransaction) for complete spec + components: parameters: select: @@ -1460,7 +1700,7 @@ components: description: Epoch Number to fetch details for schema: type: string - example: 1950 + example: 6219 in: query required: false allowEmptyValue: true @@ -1554,6 +1794,16 @@ components: in: query required: false allowEmptyValue: true + _extended: + deprecated: false + name: _extended + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _history: deprecated: false name: _history @@ -1600,7 +1850,7 @@ components: description: Script hash in hexadecimal format (hex) schema: type: string - example: 160301a01ee86d8e46cbe3aef1e3bf69bfa28c65d5be2dde56a37af8 + example: 1392eec7d575292ae1523da65ff1b4b021886e917c8c43de54aa7cbd in: query required: true allowEmptyValue: false @@ -1661,6 +1911,29 @@ components: _addresses: - addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z - addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu + payment_addresses_with_extended: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _addresses: + - addr_test1qzmtfv43a8ncx6ve92ja6yy25npn9raz9pu5a2tfxsqv9gy9ktf0pu6yu4zjh9r37fzx3h4tsxqdjhu3t4d5ffdsfz9s6ska3z + - addr_test1vq67g5u8ls4vm4wdvs0r8xvsuej66nvaqedyrj2tcz6tuycz275pu + _extended: true address_txs: content: application/json: @@ -1733,6 +2006,32 @@ components: _stake_addresses: - stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2 - stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd + _first_only: false + _empty: false + + stake_addresses_with_extended: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _stake_addresses: + - stake_test17zt9x005zkd2usz2vhvktyzqsuwz25gmgnaqdka5hcj9m2qfg2py2 + - stake_test1uzzm95hs7dzw23ftj3cly3rgm64crqxet7g46k6y5kcy3zcs3mpjd + _extended: true stake_addresses: content: application/json: @@ -1788,10 +2087,15 @@ components: items: type: string description: Array of Cardano payment credential(s) in hex format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _payment_credentials: - b6b4b2b1e9e78369992aa5dd108aa4c3328fa228794ea9693400c2a0 - 35e45387fc2acdd5cd641e339990e665ad4d9d065a41c94bc0b4be13 + _extended: true tx_ids: content: application/json: @@ -1853,6 +2157,22 @@ components: - pool19st4a2vvu78tjtyywjte2eml3kx6ynersgd2nyw0y4jvyhlfu0u - pool1uul8pytp2p0xq4ckjn3l294km0m7fuef46teehvh3x5tk46sfx3 - pool1us9ww725p0vygae5zaydme3apt7wg9se2yemhl8mgwkes3tlrqp + script_hashes: + content: + application/json: + schema: + type: object + properties: + _script_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano script hashes + example: + _script_hashes: + - a08a267e92456ba48e157dd7e77bdd35aba0fc50fe625a10a6a7fc5e + - 1f3a4aa08cfa0e47fff200578f0d4847b6890b7093a765773feb35de datum_hashes: content: application/json: @@ -1866,10 +2186,30 @@ components: type: string description: Array of Cardano datum hashes example: - _datum_hashes: - - 964af1ff2a66ce472d34ac39b47f356b6d971d62c794a89ec12825d5de30f3aa - - 17bdb9c96b77c7718d546be50193a80bc9d72081b7375e5e16891db196af14fc - asset_list: + _datum_hashes: + - 964af1ff2a66ce472d34ac39b47f356b6d971d62c794a89ec12825d5de30f3aa + - 17bdb9c96b77c7718d546be50193a80bc9d72081b7375e5e16891db196af14fc + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e'] + - ['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e'] + asset_list_with_extended: content: application/json: schema: @@ -1885,11 +2225,43 @@ components: type: array items: type: string + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _asset_list: - ['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e'] - ['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e'] - securitySchemes: {} + _extended: true + utxo_refs_with_extended: + content: + application/json: + schema: + required: + - _utxo_refs + type: object + properties: + _utxo_refs: + format: text + type: array + items: + type: string + description: Array of Cardano utxo references in the form "hash#index" + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _utxo_refs: + - f82e568d42604fd71424d193c86ec00c97aead2b8f018e81c3139d9e3770c735#0 + - 88ae22495123c7ee37a0bbe865243757185a302ed5359d1eae9347030628290a#0 + _extended: false + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT schemas: tip: type: array @@ -2003,20 +2375,59 @@ components: type: string description: JSON encoded data with details about the parameter update example: {"decentralisation": 0.9} + reserve_withdrawals: + type: array + items: + properties: + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" pool_list: type: array items: properties: pool_id_bech32: - type: string - nullable: true - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + pool_id_hex: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" + active_epoch_no: + $ref: "#/components/schemas/pool_info/items/properties/active_epoch_no" + margin: + $ref: "#/components/schemas/pool_info/items/properties/margin" + fixed_cost: + $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" + pledge: + $ref: "#/components/schemas/pool_info/items/properties/pledge" + reward_addr: + $ref: "#/components/schemas/pool_info/items/properties/reward_addr" + owners: + $ref: "#/components/schemas/pool_info/items/properties/margin" + relays: + $ref: "#/components/schemas/pool_info/items/properties/margin" ticker: type: string nullable: true description: Pool ticker - example: JAZZ + example: AHL + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/margin" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/margin" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/margin" + retiring_epoch: + $ref: "#/components/schemas/pool_info/items/properties/margin" pool_history_info: type: array items: @@ -2088,26 +2499,32 @@ components: $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string + nullable: true description: Pool VRF key hash example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 margin: type: number + nullable: true description: Margin (decimal format) example: 0.1 fixed_cost: type: string + nullable: true description: Pool fixed cost per epoch example: "500000000" pledge: type: string + nullable: true description: Pool pledge in lovelace example: "64000000000000" reward_addr: type: string + nullable: true description: Pool reward address example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 owners: type: array + nullable: true items: type: string description: Pool (co)owner address @@ -2270,6 +2687,26 @@ components: type: string description: Latest transaction hash used for delegation by the account example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_registrations: + type: array + nullable: true + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + active_epoch_no: + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" pool_delegators_history: type: array nullable: true @@ -2318,6 +2755,7 @@ components: active_epoch_no: type: integer description: Epoch number in which the update becomes active + nullable: true example: 324 vrf_key_hash: $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" @@ -2339,8 +2777,11 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" - pool_status: - $ref: "#/components/schemas/pool_info/items/properties/pool_status" + update_type: + type: string + description: Type of update task + enum: ["registration", "deregistration"] + example: registered retiring_epoch: $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" pool_relays: @@ -2352,6 +2793,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" relays: $ref: "#/components/schemas/pool_info/items/properties/relays" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" pool_metadata: type: array items: @@ -2365,6 +2808,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" epoch_info: type: array items: @@ -2533,8 +2978,8 @@ components: description: The hash of the first block where these parameters are valid example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 cost_models: - type: string - description: The per language cost models + type: object + description: The per language cost model in JSON example: null nullable: true price_mem: @@ -2724,17 +3169,21 @@ components: properties: block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" - tx_hashes: - type: array - items: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" address_info: type: array items: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2753,9 +3202,9 @@ components: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: @@ -2769,7 +3218,7 @@ components: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" address_txs: type: array items: @@ -2789,22 +3238,17 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" - asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" - credential_txs: - $ref: "#/components/schemas/address_txs" - credential_utxos: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" - tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" - value: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + $ref: "#/components/schemas/utxo_infos/items/properties/address" + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_list: type: array items: @@ -2825,7 +3269,9 @@ components: enum: ["registered", "not registered"] example: registered delegated_pool: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + nullable: true + allOf: + - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" total_balance: type: string description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) @@ -2854,23 +3300,67 @@ components: type: string description: Total treasury MIR value of the account example: "0" - account_utxos: + utxo_infos: type: array items: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + type: string + description: Hash identifier of the transaction + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + type: integer + description: Index of UTxO in the transaction + example: 0 address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + type: string + description: A Cardano payment/base address (bech32 encoded) + example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp value: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + stake_address: + $ref: "#/components/schemas/address_info/items/properties/stake_address" + payment_cred: + type: string + description: Payment credential + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 + nullable: true + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" + datum_hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" + asset_list: + type: array + nullable: true + description: An array of assets on the UTxO + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + type: string + description: Quantity of assets on the UTxO + example: 1 + is_spent: + type: boolean + description: True if the UTXO has been spent + example: true + account_rewards: type: array items: @@ -2915,7 +3405,7 @@ components: enum: ["registration", "delegation", "withdrawal", "deregistration"] example: "registration" tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" epoch_no: $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: @@ -2934,7 +3424,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" account_assets: type: array items: @@ -2942,25 +3432,16 @@ components: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - asset_list: - type: array - items: - type: object - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - type: integer - description: Asset decimals - example: 6 - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_history: type: array items: @@ -2992,9 +3473,7 @@ components: type: object properties: tx_hash: - type: string - description: Hash identifier of the transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" block_height: @@ -3061,25 +3540,17 @@ components: type: object properties: bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw + $ref: "#/components/schemas/utxo_infos/items/properties/address" cred: type: string description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3129,23 +3600,7 @@ components: description: Value (json) example: null asset_list: - type: array - nullable: true - description: An array of assets on the UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - quantity: - type: string - description: Quantity of assets on the UTxO - example: 1 + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -3174,7 +3629,7 @@ components: fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3212,7 +3667,7 @@ components: items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" script_json: type: object description: JSON representation of the timelock script (null for other script types) @@ -3237,6 +3692,7 @@ components: } plutus_contracts: type: array + nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -3246,15 +3702,11 @@ components: example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 nullable: true script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" bytecode: - type: string - description: CBOR-encoded Plutus script data - example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 + $ref: "#/components/schemas/script_info/items/properties/bytes" size: - type: integer - description: The size of the CBOR serialised script (in bytes) - example: 234895 + $ref: "#/components/schemas/script_info/items/properties/size" valid_contract: type: boolean description: True if the contract is valid or there is no contract @@ -3314,17 +3766,11 @@ components: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3339,7 +3785,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" metadata: type: object nullable: true @@ -3360,7 +3806,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -3414,9 +3860,7 @@ components: items: properties: payment_address: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: type: string description: Asset balance on the payment address @@ -3427,7 +3871,7 @@ components: items: properties: payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" asset_summary: type: array items: @@ -3519,6 +3963,11 @@ components: decimals: type: integer example: 0 + cip68_metadata: + type: object + description: CIP 68 metadata if present for asset + nullable: true + example: {"222": {"fields": [{"map": [{"k": {"bytes": "6e616d65"}, "v": {"bytes": "74657374"}}]}], "constructor": 0}} asset_history: type: array items: @@ -3559,7 +4008,7 @@ components: asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: $ref: "#/components/schemas/asset_addresses/items/properties/quantity" policy_asset_info: @@ -3599,67 +4048,65 @@ components: total_supply: $ref: "#/components/schemas/asset_info/items/properties/total_supply" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - asset_txs: - type: array - description: An array of Tx hashes that included the given asset (latest first) - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - native_script_list: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + script_info: type: array items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + type: string + description: Hash of a script + example: bfa7ffa9b2e164873db6ac6d0528c82e212963bc62e10fd1d81da4af creation_tx_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" + type: string + description: Hash of the script creation transaction + example: 255f061502ad83230351fbcf2d9fade1b5d118d332f92c9861075010a1fe3fbe type: type: string description: Type of the script - enum: ["timelock", "multisig"] - example: timelock - plutus_script_list: + enum: ["plutusV1","plutusV2","timelock","multisig"] + example: plutusV1 + value: + type: object + nullable: true + description: Data in JSON format + example: null + bytes: + type: string + description: Script bytes (cborSeq) + example: 5907f4010000332323232323232323233223232323232332232323232322223232533532533533355300712001323212330012233350052200200200100235001220011233001225335002101710010142325335333573466e3cd400488008d4020880080580544ccd5cd19b873500122001350082200101601510153500122002353500122002222222222200a101413357389201115554784f206e6f7420636f6e73756d6564000133333573466e1cd55cea8012400046644246600200600464646464646464646464646666ae68cdc39aab9d500a480008cccccccccc888888888848cccccccccc00402c02802402001c01801401000c008cd40508c8c8cccd5cd19b8735573aa0049000119910919800801801180f9aba150023019357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854028cd4050054d5d0a804999aa80bbae501635742a010666aa02eeb94058d5d0a80399a80a0109aba15006335014335502402275a6ae854014c8c8c8cccd5cd19b8735573aa00490001199109198008018011919191999ab9a3370e6aae754009200023322123300100300233502575a6ae854008c098d5d09aba2500223263533573805e05c05a05826aae7940044dd50009aba150023232323333573466e1cd55cea8012400046644246600200600466a04aeb4d5d0a80118131aba135744a004464c6a66ae700bc0b80b40b04d55cf280089baa001357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854010cd4051d71aba15003335014335502475c40026ae854008c070d5d09aba2500223263533573804e04c04a04826ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d400520062321222230040053019357426aae79400c8cccd5cd19b875002480108c848888c008014c06cd5d09aab9e500423333573466e1d400d20022321222230010053015357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6a66ae7008808408007c0780740704d55cea80089baa001357426ae8940088c98d4cd5ce00d80d00c80c080c89931a99ab9c4910350543500019018135573ca00226ea8004c8004d5405888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000448c88c008dd6000990009aa80b111999aab9f00125009233500830043574200460066ae880080548c8c8c8cccd5cd19b8735573aa00690001199911091998008020018011919191999ab9a3370e6aae7540092000233221233001003002301735742a00466a01c02c6ae84d5d1280111931a99ab9c01b01a019018135573ca00226ea8004d5d0a801999aa803bae500635742a00466a014eb8d5d09aba2500223263533573802e02c02a02826ae8940044d55cf280089baa0011335500175ceb44488c88c008dd5800990009aa80a11191999aab9f0022500823350073355017300635573aa004600a6aae794008c010d5d100180a09aba100111220021221223300100400312232323333573466e1d4005200023212230020033005357426aae79400c8cccd5cd19b8750024800884880048c98d4cd5ce00980900880800789aab9d500113754002464646666ae68cdc39aab9d5002480008cc8848cc00400c008c014d5d0a8011bad357426ae8940088c98d4cd5ce00800780700689aab9e5001137540024646666ae68cdc39aab9d5001480008dd71aba135573ca004464c6a66ae7003803403002c4dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6a66ae7004404003c0380340304d55cea80089baa0012323333573466e1d40052002200523333573466e1d40092000200523263533573801a01801601401226aae74dd5000891001091000919191919191999ab9a3370ea002900610911111100191999ab9a3370ea004900510911111100211999ab9a3370ea00690041199109111111198008048041bae35742a00a6eb4d5d09aba2500523333573466e1d40112006233221222222233002009008375c6ae85401cdd71aba135744a00e46666ae68cdc3a802a400846644244444446600c01201060186ae854024dd71aba135744a01246666ae68cdc3a8032400446424444444600e010601a6ae84d55cf280591999ab9a3370ea00e900011909111111180280418071aba135573ca018464c6a66ae7004c04804404003c03803403002c0284d55cea80209aab9e5003135573ca00426aae7940044dd50009191919191999ab9a3370ea002900111999110911998008028020019bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c8488c00800cc020d5d09aab9e500623263533573801801601401201026aae75400c4d5d1280089aab9e500113754002464646666ae68cdc3a800a400446424460020066eb8d5d09aab9e500323333573466e1d400920002321223002003375c6ae84d55cf280211931a99ab9c009008007006005135573aa00226ea800444888c8c8cccd5cd19b8735573aa0049000119aa80518031aba150023005357426ae8940088c98d4cd5ce00480400380309aab9e5001137540029309000a490350543100112212330010030021123230010012233003300200200133512233002489209366f09fe40eaaeb17d3cb6b0b61e087d664174c39a48a986f86b2b0ba6e2a7b00480008848cc00400c0088005 + size: + type: integer + description: The size of the CBOR serialised script (in bytes) + example: 2039 + script_list: type: array items: properties: script_hash: - type: string - description: Hash of a script - example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 + $ref: "#/components/schemas/script_info/items/properties/script_hash" creation_tx_hash: - type: string - description: Hash of the script creation transaction - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" + type: + $ref: "#/components/schemas/script_info/items/properties/type" + size: + $ref: "#/components/schemas/script_info/items/properties/size" script_redeemers: type: array items: type: object properties: script_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/script_info/items/properties/script_hash" redeemers: type: array items: type: object properties: tx_hash: - type: string - description: Hash of Transaction containing the redeemer - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: The index of the redeemer pointer in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" unit_mem: description: The budget in Memory to run a script example: 520448 @@ -3691,20 +4138,20 @@ components: nullable: true example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 datum_value: - type: object - description: The actual data in json format - example: { "bytes": "3c33" } + $ref: "#/components/schemas/script_info/items/properties/value" datum_info: type: array items: type: object properties: - hash: + datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + creation_tx_hash: + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + $ref: "#/components/schemas/script_info/items/properties/value" bytes: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" + $ref: "#/components/schemas/script_info/items/properties/bytes" headers: {} responses: OK: @@ -3712,7 +4159,7 @@ components: NotFound: description: The server does not recognise the combination of endpoint and parameters provided Unauthorized: - description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint + description: Access token is missing or invalid PartialContent: description: The result was truncated BadRequest: @@ -3730,12 +4177,12 @@ tags: - name: Transactions description: Query blockchain transaction details x-tag-expanded: false + - name: Stake Account + description: Query details about specific stake account addresses + x-tag-expanded: false - name: Address description: Query information about specific address(es) x-tag-expanded: false - - name: Account - description: Query details about specific stake account addresses - x-tag-expanded: false - name: Asset description: Query Asset related informations x-tag-expanded: false @@ -3745,4 +4192,5 @@ tags: - name: Script description: Query information about specific scripts (Smart Contracts) x-tag-expanded: false -security: [] +security: + - bearerAuth: [] diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index f9d5c3e0..f8005196 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.10 + version: v1.1.0rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. @@ -14,11 +14,11 @@ info: Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

    ``` bash - curl "https://api.koios.rest/api/v0/tip" + curl "https://api.koios.rest/api/v1/tip" # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] - curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" + curl "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_height" # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] ``` @@ -27,7 +27,7 @@ info: You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

    ``` bash - curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" + curl "https://api.koios.rest/api/v1/blocks?epoch=eq.250&epoch_slot=lt.180" # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] @@ -62,7 +62,7 @@ info: Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -I | grep -i content-range # content-range: 0-999/* @@ -71,7 +71,7 @@ info: As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range # content-range: 1000-1499/* @@ -80,7 +80,7 @@ info: For GET endpoints, there is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range # content-range: 1000-1499/* @@ -94,7 +94,7 @@ info: Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" + curl -s "https://api.koios.rest/api/v1/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, @@ -109,13 +109,13 @@ info: Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" # epoch,epoch_slot,block_time # 318,28491,1643607582 @@ -134,6 +134,10 @@ info: Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. + # Authentication + + While Koios public tier remains unauthenticated and allows queries without any authentication, it has low limits to prevent actions against an erroraneous query/loop from a consumer. There is also a Free tier which requires setting up Bearer Auth token that is linked to the owner's wallet account (which can be connected to via [Koios website](https://koios.rest/pricing/Pricing.html) ). + The examples across this API site already [supports authentication](/#auth), for you to use in the queries. # Community projects @@ -142,11 +146,12 @@ info: x-logo: url: "https://api.koios.rest/images/koios.png" servers: - - url: https://api.koios.rest/api/v0 - - url: https://guild.koios.rest/api/v0 - - url: https://preview.koios.rest/api/v0 - - url: https://preprod.koios.rest/api/v0 + - url: https://api.koios.rest/api/v1 + - url: https://guild.koios.rest/api/v1 + - url: https://preview.koios.rest/api/v1 + - url: https://preprod.koios.rest/api/v1 paths: + /tip: #RPC get: tags: @@ -227,6 +232,45 @@ paths: $ref: "#/components/responses/NotFound" summary: Param Update Proposals description: Get all parameter update proposals submitted to the chain starting Shelley era + /reserve_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from reserves against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Reserve Withdrawals + description: List of all withdrawals from reserves against stake accounts + /treasury_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from treasury against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Treasury Withdrawals + description: List of all withdrawals from treasury against stake accounts + /epoch_info: #RPC get: tags: @@ -294,6 +338,7 @@ paths: summary: Epoch's Block Protocols description: >- Get the information about block protocol distribution in epoch + /blocks: get: tags: @@ -355,28 +400,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Block Transactions description: Get a list of all transactions included in provided blocks - /tx_info: #RPC + + /utxo_info: #RPC post: tags: - Transactions requestBody: - $ref: "#/components/requestBodies/tx_ids" + $ref: "#/components/requestBodies/utxo_refs_with_extended" responses: "200": - description: Array of detailed information about transaction(s) + description: Array of UTXO details content: application/json: schema: - $ref: "#/components/schemas/tx_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Information - description: Get detailed information about transaction(s) - /tx_utxos: #RPC + summary: UTxO Info + description: Get UTxO set for requested UTxO references + /tx_info: #RPC post: tags: - Transactions @@ -384,19 +430,19 @@ paths: $ref: "#/components/requestBodies/tx_ids" responses: "200": - description: Array of inputs and outputs for given transaction(s) + description: Array of detailed information about transaction(s) content: application/json: schema: - $ref: "#/components/schemas/tx_utxos" + $ref: "#/components/schemas/tx_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction UTxOs [DEPRECATED] - description: Get UTxO set (inputs/outputs) of transactions. + summary: Transaction Information + description: Get detailed information about transaction(s) /tx_metadata: #RPC post: tags: @@ -450,7 +496,7 @@ paths: # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. curl -X POST \ --header "Content-Type: application/cbor" \ - --data-binary ${data} https://api.koios.rest/api/v0/submittx + --data-binary @${data} https://api.koios.rest/api/v1/submittx responses: "202": description: OK @@ -486,8 +532,31 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Status (Block Confirmations) + summary: Transaction Status description: Get the number of block confirmations for a given transaction hash list + /tx_utxos: #RPC + post: + tags: + - Transactions + deprecated: true + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of inputs and outputs for given transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_utxos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction UTxOs + description: Get UTxO set (inputs/outputs) of transactions [DEPRECATED - Use /utxo_info instead]. + /address_info: #RPC post: tags: @@ -509,27 +578,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Address Information description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses - /address_txs: #RPC + /address_utxos: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/address_txs" + $ref: "#/components/requestBodies/payment_addresses_with_extended" responses: "200": - description: Array of transaction hashes + description: Array of address UTXOs content: application/json: schema: - $ref: "#/components/schemas/address_txs" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Transactions - description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) + summary: Address UTXOs + description: Get UTxO set for given addresses /credential_utxos: #RPC post: tags: @@ -542,7 +611,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_utxos" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": @@ -550,28 +619,28 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: UTxOs from payment credentials - description: Get a list of UTxO against input payment credential array including their balances - /address_assets: #RPC + description: Get UTxO details for requested payment credentials + /address_txs: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/payment_addresses" + $ref: "#/components/requestBodies/address_txs" responses: "200": - description: Array of address-owned assets + description: Array of transaction hashes content: application/json: schema: - $ref: "#/components/schemas/address_assets" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for given addresses + summary: Address Transactions + description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /credential_txs: #RPC post: tags: @@ -584,7 +653,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_txs" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": @@ -593,6 +662,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Transactions from payment credentials description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) + /address_assets: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address-owned assets + content: + application/json: + schema: + $ref: "#/components/schemas/address_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Assets + description: Get the list of all the assets (policy, name and quantity) for given addresses + /account_list: get: tags: @@ -633,48 +724,70 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses - /account_utxos: #RPC - get: + /account_info_cached: #RPC + post: tags: - Stake Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses" responses: "200": - description: Array of account UTxOs associated with stake address + description: Array of account information content: application/json: schema: - $ref: "#/components/schemas/account_utxos" + $ref: "#/components/schemas/account_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account UTxOs - description: Get a list of all UTxOs for a given stake address (account) - /account_info_cached: #RPC + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (effective for performance query against registered accounts) + /account_utxos: #RPC post: tags: - Stake Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_extended" responses: "200": - description: Array of account information + description: Array of account UTxOs associated with given stake addresses content: application/json: schema: - $ref: "#/components/schemas/account_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account Information (Cached) - description: Get the cached account information for given stake addresses, effective for registered accounts + summary: UTxOs for stake addresses (accounts) + description: Get a list of all UTxOs for given stake addresses (account)s + /account_txs: #RPC + get: + tags: + - Stake Account + parameters: + - $ref: "#/components/parameters/_stake_address" + - $ref: "#/components/parameters/_after_block_height" + responses: + "200": + description: Array of Txs associated with stake address (account) + content: + application/json: + schema: + $ref: "#/components/schemas/address_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Txs + description: Get a list of all Txs for a given stake address (account) /account_rewards: #RPC post: tags: @@ -783,6 +896,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Account History description: Get the staking history of given stake addresses (accounts) + /asset_list: get: tags: @@ -802,95 +916,89 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) - /asset_token_registry: + /policy_asset_list: #RPC get: tags: - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" responses: "200": - description: Array of token registry information for each asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_token_registry" + $ref: "#/components/schemas/policy_asset_list" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Token Registry - description: Get a list of assets registered via token registry on github - /asset_addresses: #RPC + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) + /asset_token_registry: get: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of token registry information for each asset content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_token_registry" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Addresses - description: Get the list of all addresses holding a given asset

    - `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects - with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to - query layers to have a dedicated cache table for themselves served via Koios.` - /asset_address_list: #RPC - get: + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github + /asset_info: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + requestBody: + $ref: "#/components/requestBodies/asset_list" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of detailed asset information content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Address List [DEPRECATED] - description: Get the list of all addresses holding a given asset (replaced by asset_addresses) - /asset_nft_address: #RPC - get: + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata + /asset_utxos: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy_nft" - - $ref: "#/components/parameters/_asset_name_nft" + requestBody: + $ref: "#/components/requestBodies/asset_list_with_extended" responses: "200": - description: Payment addresses currently holding the given NFT + description: Array of UTXOs for given asset list content: application/json: schema: - $ref: "#/components/schemas/asset_nft_address" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: NFT Address - description: Get the address where specified NFT currently reside on. - /asset_info: #RPC + summary: Asset UTXOs + description: Get the UTXO information of a list of assets including + /asset_history: #RPC get: tags: - Asset @@ -899,61 +1007,66 @@ paths: - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of asset mint/burn history content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_history" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information - description: Get the information of an asset including first minting & token registry metadata - post: + summary: Asset History + description: Get the mint/burn history of an asset + /asset_addresses: #RPC + get: tags: - Asset - requestBody: - $ref: "#/components/requestBodies/asset_list" + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information (Bulk) - description: Get the information of a list of assets including first minting & token registry metadata - /asset_history: #RPC + summary: Asset Addresses + description: Get the list of all addresses holding a given asset

    + `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects + with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to + query layers to have a dedicated cache table for themselves served via Koios.` + /asset_nft_address: #RPC get: tags: - Asset parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" responses: "200": - description: Array of asset mint/burn history + description: Payment addresses currently holding the given NFT content: application/json: schema: - $ref: "#/components/schemas/asset_history" + $ref: "#/components/schemas/asset_nft_address" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset History - description: Get the mint/burn history of an asset + summary: NFT Address + description: Get the address where specified NFT currently reside on. /policy_asset_addresses: #RPC get: tags: @@ -999,94 +1112,98 @@ paths: $ref: "#/components/responses/NotFound" summary: Policy Asset Information description: Get the information for all assets under the same policy - /asset_policy_info: #RPC + /asset_summary: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed information of assets under the same policy + description: Array of asset summary information content: application/json: schema: - $ref: "#/components/schemas/policy_asset_info" + $ref: "#/components/schemas/asset_summary" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information [DEPRECATED] - description: Get the information for all assets under the same policy (replaced by asset_addresses) - /policy_asset_list: #RPC + summary: Asset Summary + description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) + /asset_txs: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": - description: Array of detailed information of assets under the same policy + description: An array of Tx hashes that included the given asset (latest first) content: application/json: schema: - $ref: "#/components/schemas/policy_asset_list" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Policy Asset List - description: Get the list of asset under the given policy (including balances) - /asset_summary: #RPC + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) + /asset_address_list: #RPC get: tags: - Asset + deprecated: true parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of asset summary information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_summary" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Summary - description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) - /asset_txs: #RPC + summary: Asset Address List + description: Get the list of all addresses holding a given asset [DEPRECATED - replaced by asset_addresses] + /asset_policy_info: #RPC get: + deprecated: true tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - - $ref: "#/components/parameters/_after_block_height" - - $ref: "#/components/parameters/_history" responses: "200": - description: Array of Tx hashes that included the given asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_txs" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transactions - description: Get the list of current or all asset transaction hashes (newest first) + summary: Asset Policy Information + description: Get the information for all assets under the same policy (DEPRECATED - replaced by policy_asset_info) + /pool_list: #RPC get: tags: @@ -1105,7 +1222,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool List - description: A list of all currently registered/retiring (not retired) pools + description: List of brief info for all pools /pool_info: #RPC post: tags: @@ -1261,6 +1378,48 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Updates (History) description: Return all pool updates for all pools or only updates for specific pool if specified + /pool_registrations: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Registrations + description: Return all pool registrations initiated in the requested epoch + /pool_retirements: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Retirements + description: Return all pool retirements initiated in the requested epoch /pool_relays: #RPC get: tags: @@ -1279,7 +1438,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Relays - description: A list of registered relays for all currently registered/retiring (not retired) pools + description: A list of registered relays for all pools /pool_metadata: #RPC post: tags: @@ -1300,7 +1459,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Metadata - description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools + description: Metadata (on & off-chain) for all pools + + /script_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/script_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/script_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes /native_script_list: #RPC get: tags: @@ -1311,7 +1492,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/native_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1330,7 +1511,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/plutus_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1360,6 +1541,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /script_utxos: #RPC + get: + tags: + - Script + parameters: + - $ref: "#/components/parameters/_script_hash" + - $ref: "#/components/parameters/_extended" + responses: + "200": + description: List of UTXOs for a given script hash + content: + application/json: + schema: + $ref: "#/components/schemas/utxo_infos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Script UTXOs + description: List of all UTXOs for a given script hash /datum_info: #RPC post: tags: @@ -1381,6 +1584,43 @@ paths: $ref: "#/components/responses/NotFound" summary: Datum Information description: List of datum information for given datum hashes + /ogmios/?EvaluateTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains EvaluateTransaction payload as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?EvaluateTransaction + responses: + "200": + description: OK + "400": + description: An error occured while submitting transaction. + summary: Evaluate Transaction + description: Evaluate execution units of scripts in a well-formed transaction. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?EvaluateTransaction) for complete spec + /ogmios/?SubmitTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains a CBOR-serialized signed transaction (base16-encoded) as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?SubmitTransaction + responses: + "200": + description: OK + "400": + description: An error occured while querying transaction. + summary: Submit Transaction + description: Submit a signed and serialized transaction to the network. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?SubmitTransaction) for complete spec + components: parameters: select: @@ -1554,6 +1794,16 @@ components: in: query required: false allowEmptyValue: true + _extended: + deprecated: false + name: _extended + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _history: deprecated: false name: _history @@ -1661,6 +1911,29 @@ components: _addresses: - addr1qy2jt0qpqz2z2z9zx5w4xemekkce7yderz53kjue53lpqv90lkfa9sgrfjuz6uvt4uqtrqhl2kj0a9lnr9ndzutx32gqleeckv - addr1q9xvgr4ehvu5k5tmaly7ugpnvekpqvnxj8xy50pa7kyetlnhel389pa4rnq6fmkzwsaynmw0mnldhlmchn2sfd589fgsz9dd0y + payment_addresses_with_extended: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _addresses: + - addr1qy2jt0qpqz2z2z9zx5w4xemekkce7yderz53kjue53lpqv90lkfa9sgrfjuz6uvt4uqtrqhl2kj0a9lnr9ndzutx32gqleeckv + - addr1q9xvgr4ehvu5k5tmaly7ugpnvekpqvnxj8xy50pa7kyetlnhel389pa4rnq6fmkzwsaynmw0mnldhlmchn2sfd589fgsz9dd0y + _extended: true address_txs: content: application/json: @@ -1706,7 +1979,7 @@ components: _stake_addresses: - stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250 - stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy - _epoch_no: 350 + _epoch_no: 409 stake_addresses_with_first_only_and_empty: content: application/json: @@ -1733,6 +2006,32 @@ components: _stake_addresses: - stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250 - stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy + _first_only: false + _empty: false + + stake_addresses_with_extended: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _stake_addresses: + - stake1uyrx65wjqjgeeksd8hptmcgl5jfyrqkfq0xe8xlp367kphsckq250 + - stake1uxpdrerp9wrxunfh6ukyv5267j70fzxgw0fr3z8zeac5vyqhf9jhy + _extended: true stake_addresses: content: application/json: @@ -1788,10 +2087,15 @@ components: items: type: string description: Array of Cardano payment credential(s) in hex format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _payment_credentials: - 025b0a8f85cb8a46e1dda3fae5d22f07e2d56abb4019a2129c5d6c52 - 13f6870c5e4f3b242463e4dc1f2f56b02a032d3797d933816f15e555 + _extended: true tx_ids: content: application/json: @@ -1853,6 +2157,22 @@ components: - pool100wj94uzf54vup2hdzk0afng4dhjaqggt7j434mtgm8v2gfvfgp - pool102s2nqtea2hf5q0s4amj0evysmfnhrn4apyyhd4azcmsclzm96m - pool102vsulhfx8ua2j9fwl2u7gv57fhhutc3tp6juzaefgrn7ae35wm + script_hashes: + content: + application/json: + schema: + type: object + properties: + _script_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano script hashes + example: + _script_hashes: + - bd2119ee2bfb8c8d7c427e8af3c35d537534281e09e23013bca5b138 + - c0c671fba483641a71bb92d3a8b7c52c90bf1c01e2b83116ad7d4536 datum_hashes: content: application/json: @@ -1866,10 +2186,30 @@ components: type: string description: Array of Cardano datum hashes example: - _datum_hashes: - - 818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46 - - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 - asset_list: + _datum_hashes: + - 818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46 + - 45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0 + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501','424f4f4b'] + - ['f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a','6b6f696f732e72657374'] + asset_list_with_extended: content: application/json: schema: @@ -1885,11 +2225,43 @@ components: type: array items: type: string + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _asset_list: - ['750900e4999ebe0d58f19b634768ba25e525aaf12403bfe8fe130501','424f4f4b'] - - ['1d7f33bd23d85e1a25d87d86fac4f199c3197a2f7afeb662a0f34e1e','776f726c646d6f62696c65746f6b656e'] - securitySchemes: {} + - ['f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a','6b6f696f732e72657374'] + _extended: true + utxo_refs_with_extended: + content: + application/json: + schema: + required: + - _utxo_refs + type: object + properties: + _utxo_refs: + format: text + type: array + items: + type: string + description: Array of Cardano utxo references in the form "hash#index" + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _utxo_refs: + - f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e#0 + - 0b8ba3bed976fa4913f19adc9f6dd9063138db5b4dd29cecde369456b5155e94#0 + _extended: false + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT schemas: tip: type: array @@ -2003,20 +2375,59 @@ components: type: string description: JSON encoded data with details about the parameter update example: {"decentralisation": 0.9} + reserve_withdrawals: + type: array + items: + properties: + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" pool_list: type: array items: properties: pool_id_bech32: - type: string - nullable: true - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + pool_id_hex: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" + active_epoch_no: + $ref: "#/components/schemas/pool_info/items/properties/active_epoch_no" + margin: + $ref: "#/components/schemas/pool_info/items/properties/margin" + fixed_cost: + $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" + pledge: + $ref: "#/components/schemas/pool_info/items/properties/pledge" + reward_addr: + $ref: "#/components/schemas/pool_info/items/properties/reward_addr" + owners: + $ref: "#/components/schemas/pool_info/items/properties/margin" + relays: + $ref: "#/components/schemas/pool_info/items/properties/margin" ticker: type: string nullable: true description: Pool ticker - example: JAZZ + example: AHL + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/margin" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/margin" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/margin" + retiring_epoch: + $ref: "#/components/schemas/pool_info/items/properties/margin" pool_history_info: type: array items: @@ -2088,26 +2499,32 @@ components: $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string + nullable: true description: Pool VRF key hash example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 margin: type: number + nullable: true description: Margin (decimal format) example: 0.1 fixed_cost: type: string + nullable: true description: Pool fixed cost per epoch example: "500000000" pledge: type: string + nullable: true description: Pool pledge in lovelace example: "64000000000000" reward_addr: type: string + nullable: true description: Pool reward address example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 owners: type: array + nullable: true items: type: string description: Pool (co)owner address @@ -2270,6 +2687,26 @@ components: type: string description: Latest transaction hash used for delegation by the account example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_registrations: + type: array + nullable: true + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + active_epoch_no: + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" pool_delegators_history: type: array nullable: true @@ -2318,6 +2755,7 @@ components: active_epoch_no: type: integer description: Epoch number in which the update becomes active + nullable: true example: 324 vrf_key_hash: $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" @@ -2339,8 +2777,11 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" - pool_status: - $ref: "#/components/schemas/pool_info/items/properties/pool_status" + update_type: + type: string + description: Type of update task + enum: ["registration", "deregistration"] + example: registered retiring_epoch: $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" pool_relays: @@ -2352,6 +2793,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" relays: $ref: "#/components/schemas/pool_info/items/properties/relays" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" pool_metadata: type: array items: @@ -2365,6 +2808,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" epoch_info: type: array items: @@ -2533,8 +2978,8 @@ components: description: The hash of the first block where these parameters are valid example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 cost_models: - type: string - description: The per language cost models + type: object + description: The per language cost model in JSON example: null nullable: true price_mem: @@ -2724,17 +3169,21 @@ components: properties: block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" - tx_hashes: - type: array - items: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" address_info: type: array items: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2753,9 +3202,9 @@ components: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: @@ -2769,7 +3218,7 @@ components: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" address_txs: type: array items: @@ -2789,22 +3238,17 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" - asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" - credential_txs: - $ref: "#/components/schemas/address_txs" - credential_utxos: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" - tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" - value: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + $ref: "#/components/schemas/utxo_infos/items/properties/address" + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_list: type: array items: @@ -2825,7 +3269,9 @@ components: enum: ["registered", "not registered"] example: registered delegated_pool: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + nullable: true + allOf: + - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" total_balance: type: string description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) @@ -2854,23 +3300,67 @@ components: type: string description: Total treasury MIR value of the account example: "0" - account_utxos: + utxo_infos: type: array items: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + type: string + description: Hash identifier of the transaction + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + type: integer + description: Index of UTxO in the transaction + example: 0 address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + type: string + description: A Cardano payment/base address (bech32 encoded) + example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp value: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + stake_address: + $ref: "#/components/schemas/address_info/items/properties/stake_address" + payment_cred: + type: string + description: Payment credential + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 + nullable: true + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" + datum_hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" + asset_list: + type: array + nullable: true + description: An array of assets on the UTxO + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + type: string + description: Quantity of assets on the UTxO + example: 1 + is_spent: + type: boolean + description: True if the UTXO has been spent + example: true + account_rewards: type: array items: @@ -2915,7 +3405,7 @@ components: enum: ["registration", "delegation", "withdrawal", "deregistration"] example: "registration" tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" epoch_no: $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: @@ -2934,7 +3424,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" account_assets: type: array items: @@ -2942,25 +3432,16 @@ components: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - asset_list: - type: array - items: - type: object - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - type: integer - description: Asset decimals - example: 6 - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_history: type: array items: @@ -2992,9 +3473,7 @@ components: type: object properties: tx_hash: - type: string - description: Hash identifier of the transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" block_height: @@ -3061,25 +3540,17 @@ components: type: object properties: bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw + $ref: "#/components/schemas/utxo_infos/items/properties/address" cred: type: string description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3129,23 +3600,7 @@ components: description: Value (json) example: null asset_list: - type: array - nullable: true - description: An array of assets on the UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - quantity: - type: string - description: Quantity of assets on the UTxO - example: 1 + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -3174,7 +3629,7 @@ components: fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3212,7 +3667,7 @@ components: items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" script_json: type: object description: JSON representation of the timelock script (null for other script types) @@ -3237,6 +3692,7 @@ components: } plutus_contracts: type: array + nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -3246,15 +3702,11 @@ components: example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 nullable: true script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" bytecode: - type: string - description: CBOR-encoded Plutus script data - example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 + $ref: "#/components/schemas/script_info/items/properties/bytes" size: - type: integer - description: The size of the CBOR serialised script (in bytes) - example: 234895 + $ref: "#/components/schemas/script_info/items/properties/size" valid_contract: type: boolean description: True if the contract is valid or there is no contract @@ -3314,17 +3766,11 @@ components: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3339,7 +3785,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" metadata: type: object nullable: true @@ -3360,7 +3806,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -3414,9 +3860,7 @@ components: items: properties: payment_address: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: type: string description: Asset balance on the payment address @@ -3427,7 +3871,7 @@ components: items: properties: payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" asset_summary: type: array items: @@ -3519,6 +3963,11 @@ components: decimals: type: integer example: 0 + cip68_metadata: + type: object + description: CIP 68 metadata if present for asset + nullable: true + example: {"222": {"fields": [{"map": [{"k": {"bytes": "6e616d65"}, "v": {"bytes": "74657374"}}]}], "constructor": 0}} asset_history: type: array items: @@ -3559,7 +4008,7 @@ components: asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: $ref: "#/components/schemas/asset_addresses/items/properties/quantity" policy_asset_info: @@ -3599,67 +4048,65 @@ components: total_supply: $ref: "#/components/schemas/asset_info/items/properties/total_supply" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - asset_txs: - type: array - description: An array of Tx hashes that included the given asset (latest first) - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - native_script_list: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + script_info: type: array items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + type: string + description: Hash of a script + example: bfa7ffa9b2e164873db6ac6d0528c82e212963bc62e10fd1d81da4af creation_tx_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" + type: string + description: Hash of the script creation transaction + example: 255f061502ad83230351fbcf2d9fade1b5d118d332f92c9861075010a1fe3fbe type: type: string description: Type of the script - enum: ["timelock", "multisig"] - example: timelock - plutus_script_list: + enum: ["plutusV1","plutusV2","timelock","multisig"] + example: plutusV1 + value: + type: object + nullable: true + description: Data in JSON format + example: null + bytes: + type: string + description: Script bytes (cborSeq) + example: 5907f4010000332323232323232323233223232323232332232323232322223232533532533533355300712001323212330012233350052200200200100235001220011233001225335002101710010142325335333573466e3cd400488008d4020880080580544ccd5cd19b873500122001350082200101601510153500122002353500122002222222222200a101413357389201115554784f206e6f7420636f6e73756d6564000133333573466e1cd55cea8012400046644246600200600464646464646464646464646666ae68cdc39aab9d500a480008cccccccccc888888888848cccccccccc00402c02802402001c01801401000c008cd40508c8c8cccd5cd19b8735573aa0049000119910919800801801180f9aba150023019357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854028cd4050054d5d0a804999aa80bbae501635742a010666aa02eeb94058d5d0a80399a80a0109aba15006335014335502402275a6ae854014c8c8c8cccd5cd19b8735573aa00490001199109198008018011919191999ab9a3370e6aae754009200023322123300100300233502575a6ae854008c098d5d09aba2500223263533573805e05c05a05826aae7940044dd50009aba150023232323333573466e1cd55cea8012400046644246600200600466a04aeb4d5d0a80118131aba135744a004464c6a66ae700bc0b80b40b04d55cf280089baa001357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854010cd4051d71aba15003335014335502475c40026ae854008c070d5d09aba2500223263533573804e04c04a04826ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d400520062321222230040053019357426aae79400c8cccd5cd19b875002480108c848888c008014c06cd5d09aab9e500423333573466e1d400d20022321222230010053015357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6a66ae7008808408007c0780740704d55cea80089baa001357426ae8940088c98d4cd5ce00d80d00c80c080c89931a99ab9c4910350543500019018135573ca00226ea8004c8004d5405888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000448c88c008dd6000990009aa80b111999aab9f00125009233500830043574200460066ae880080548c8c8c8cccd5cd19b8735573aa00690001199911091998008020018011919191999ab9a3370e6aae7540092000233221233001003002301735742a00466a01c02c6ae84d5d1280111931a99ab9c01b01a019018135573ca00226ea8004d5d0a801999aa803bae500635742a00466a014eb8d5d09aba2500223263533573802e02c02a02826ae8940044d55cf280089baa0011335500175ceb44488c88c008dd5800990009aa80a11191999aab9f0022500823350073355017300635573aa004600a6aae794008c010d5d100180a09aba100111220021221223300100400312232323333573466e1d4005200023212230020033005357426aae79400c8cccd5cd19b8750024800884880048c98d4cd5ce00980900880800789aab9d500113754002464646666ae68cdc39aab9d5002480008cc8848cc00400c008c014d5d0a8011bad357426ae8940088c98d4cd5ce00800780700689aab9e5001137540024646666ae68cdc39aab9d5001480008dd71aba135573ca004464c6a66ae7003803403002c4dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6a66ae7004404003c0380340304d55cea80089baa0012323333573466e1d40052002200523333573466e1d40092000200523263533573801a01801601401226aae74dd5000891001091000919191919191999ab9a3370ea002900610911111100191999ab9a3370ea004900510911111100211999ab9a3370ea00690041199109111111198008048041bae35742a00a6eb4d5d09aba2500523333573466e1d40112006233221222222233002009008375c6ae85401cdd71aba135744a00e46666ae68cdc3a802a400846644244444446600c01201060186ae854024dd71aba135744a01246666ae68cdc3a8032400446424444444600e010601a6ae84d55cf280591999ab9a3370ea00e900011909111111180280418071aba135573ca018464c6a66ae7004c04804404003c03803403002c0284d55cea80209aab9e5003135573ca00426aae7940044dd50009191919191999ab9a3370ea002900111999110911998008028020019bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c8488c00800cc020d5d09aab9e500623263533573801801601401201026aae75400c4d5d1280089aab9e500113754002464646666ae68cdc3a800a400446424460020066eb8d5d09aab9e500323333573466e1d400920002321223002003375c6ae84d55cf280211931a99ab9c009008007006005135573aa00226ea800444888c8c8cccd5cd19b8735573aa0049000119aa80518031aba150023005357426ae8940088c98d4cd5ce00480400380309aab9e5001137540029309000a490350543100112212330010030021123230010012233003300200200133512233002489209366f09fe40eaaeb17d3cb6b0b61e087d664174c39a48a986f86b2b0ba6e2a7b00480008848cc00400c0088005 + size: + type: integer + description: The size of the CBOR serialised script (in bytes) + example: 2039 + script_list: type: array items: properties: script_hash: - type: string - description: Hash of a script - example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 + $ref: "#/components/schemas/script_info/items/properties/script_hash" creation_tx_hash: - type: string - description: Hash of the script creation transaction - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" + type: + $ref: "#/components/schemas/script_info/items/properties/type" + size: + $ref: "#/components/schemas/script_info/items/properties/size" script_redeemers: type: array items: type: object properties: script_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/script_info/items/properties/script_hash" redeemers: type: array items: type: object properties: tx_hash: - type: string - description: Hash of Transaction containing the redeemer - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: The index of the redeemer pointer in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" unit_mem: description: The budget in Memory to run a script example: 520448 @@ -3691,20 +4138,20 @@ components: nullable: true example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 datum_value: - type: object - description: The actual data in json format - example: { "bytes": "3c33" } + $ref: "#/components/schemas/script_info/items/properties/value" datum_info: type: array items: type: object properties: - hash: + datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + creation_tx_hash: + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + $ref: "#/components/schemas/script_info/items/properties/value" bytes: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" + $ref: "#/components/schemas/script_info/items/properties/bytes" headers: {} responses: OK: @@ -3712,7 +4159,7 @@ components: NotFound: description: The server does not recognise the combination of endpoint and parameters provided Unauthorized: - description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint + description: Access token is missing or invalid PartialContent: description: The result was truncated BadRequest: @@ -3730,12 +4177,12 @@ tags: - name: Transactions description: Query blockchain transaction details x-tag-expanded: false + - name: Stake Account + description: Query details about specific stake account addresses + x-tag-expanded: false - name: Address description: Query information about specific address(es) x-tag-expanded: false - - name: Account - description: Query details about specific stake account addresses - x-tag-expanded: false - name: Asset description: Query Asset related informations x-tag-expanded: false @@ -3745,4 +4192,5 @@ tags: - name: Script description: Query information about specific scripts (Smart Contracts) x-tag-expanded: false -security: [] +security: + - bearerAuth: [] diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 6d956f7c..222bbe72 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.10 + version: v1.1.0rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. @@ -14,11 +14,11 @@ info: Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

    ``` bash - curl "https://api.koios.rest/api/v0/tip" + curl "https://api.koios.rest/api/v1/tip" # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] - curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" + curl "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_height" # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] ``` @@ -27,7 +27,7 @@ info: You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

    ``` bash - curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" + curl "https://api.koios.rest/api/v1/blocks?epoch=eq.250&epoch_slot=lt.180" # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] @@ -62,7 +62,7 @@ info: Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -I | grep -i content-range # content-range: 0-999/* @@ -71,7 +71,7 @@ info: As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range # content-range: 1000-1499/* @@ -80,7 +80,7 @@ info: For GET endpoints, there is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range # content-range: 1000-1499/* @@ -94,7 +94,7 @@ info: Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" + curl -s "https://api.koios.rest/api/v1/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, @@ -109,13 +109,13 @@ info: Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" # epoch,epoch_slot,block_time # 318,28491,1643607582 @@ -134,6 +134,10 @@ info: Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. + # Authentication + + While Koios public tier remains unauthenticated and allows queries without any authentication, it has low limits to prevent actions against an erroraneous query/loop from a consumer. There is also a Free tier which requires setting up Bearer Auth token that is linked to the owner's wallet account (which can be connected to via [Koios website](https://koios.rest/pricing/Pricing.html) ). + The examples across this API site already [supports authentication](/#auth), for you to use in the queries. # Community projects @@ -142,11 +146,12 @@ info: x-logo: url: "https://api.koios.rest/images/koios.png" servers: - - url: https://api.koios.rest/api/v0 - - url: https://guild.koios.rest/api/v0 - - url: https://preview.koios.rest/api/v0 - - url: https://preprod.koios.rest/api/v0 + - url: https://api.koios.rest/api/v1 + - url: https://guild.koios.rest/api/v1 + - url: https://preview.koios.rest/api/v1 + - url: https://preprod.koios.rest/api/v1 paths: + /tip: #RPC get: tags: @@ -227,6 +232,45 @@ paths: $ref: "#/components/responses/NotFound" summary: Param Update Proposals description: Get all parameter update proposals submitted to the chain starting Shelley era + /reserve_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from reserves against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Reserve Withdrawals + description: List of all withdrawals from reserves against stake accounts + /treasury_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from treasury against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Treasury Withdrawals + description: List of all withdrawals from treasury against stake accounts + /epoch_info: #RPC get: tags: @@ -294,6 +338,7 @@ paths: summary: Epoch's Block Protocols description: >- Get the information about block protocol distribution in epoch + /blocks: get: tags: @@ -355,28 +400,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Block Transactions description: Get a list of all transactions included in provided blocks - /tx_info: #RPC + + /utxo_info: #RPC post: tags: - Transactions requestBody: - $ref: "#/components/requestBodies/tx_ids" + $ref: "#/components/requestBodies/utxo_refs_with_extended" responses: "200": - description: Array of detailed information about transaction(s) + description: Array of UTXO details content: application/json: schema: - $ref: "#/components/schemas/tx_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Information - description: Get detailed information about transaction(s) - /tx_utxos: #RPC + summary: UTxO Info + description: Get UTxO set for requested UTxO references + /tx_info: #RPC post: tags: - Transactions @@ -384,19 +430,19 @@ paths: $ref: "#/components/requestBodies/tx_ids" responses: "200": - description: Array of inputs and outputs for given transaction(s) + description: Array of detailed information about transaction(s) content: application/json: schema: - $ref: "#/components/schemas/tx_utxos" + $ref: "#/components/schemas/tx_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction UTxOs [DEPRECATED] - description: Get UTxO set (inputs/outputs) of transactions. + summary: Transaction Information + description: Get detailed information about transaction(s) /tx_metadata: #RPC post: tags: @@ -450,7 +496,7 @@ paths: # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. curl -X POST \ --header "Content-Type: application/cbor" \ - --data-binary ${data} https://api.koios.rest/api/v0/submittx + --data-binary @${data} https://api.koios.rest/api/v1/submittx responses: "202": description: OK @@ -486,8 +532,31 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Status (Block Confirmations) + summary: Transaction Status description: Get the number of block confirmations for a given transaction hash list + /tx_utxos: #RPC + post: + tags: + - Transactions + deprecated: true + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of inputs and outputs for given transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_utxos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction UTxOs + description: Get UTxO set (inputs/outputs) of transactions [DEPRECATED - Use /utxo_info instead]. + /address_info: #RPC post: tags: @@ -509,27 +578,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Address Information description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses - /address_txs: #RPC + /address_utxos: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/address_txs" + $ref: "#/components/requestBodies/payment_addresses_with_extended" responses: "200": - description: Array of transaction hashes + description: Array of address UTXOs content: application/json: schema: - $ref: "#/components/schemas/address_txs" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Transactions - description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) + summary: Address UTXOs + description: Get UTxO set for given addresses /credential_utxos: #RPC post: tags: @@ -542,7 +611,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_utxos" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": @@ -550,28 +619,28 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: UTxOs from payment credentials - description: Get a list of UTxO against input payment credential array including their balances - /address_assets: #RPC + description: Get UTxO details for requested payment credentials + /address_txs: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/payment_addresses" + $ref: "#/components/requestBodies/address_txs" responses: "200": - description: Array of address-owned assets + description: Array of transaction hashes content: application/json: schema: - $ref: "#/components/schemas/address_assets" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for given addresses + summary: Address Transactions + description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /credential_txs: #RPC post: tags: @@ -584,7 +653,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_txs" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": @@ -593,6 +662,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Transactions from payment credentials description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) + /address_assets: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address-owned assets + content: + application/json: + schema: + $ref: "#/components/schemas/address_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Assets + description: Get the list of all the assets (policy, name and quantity) for given addresses + /account_list: get: tags: @@ -633,48 +724,70 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses - /account_utxos: #RPC - get: + /account_info_cached: #RPC + post: tags: - Stake Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses" responses: "200": - description: Array of account UTxOs associated with stake address + description: Array of account information content: application/json: schema: - $ref: "#/components/schemas/account_utxos" + $ref: "#/components/schemas/account_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account UTxOs - description: Get a list of all UTxOs for a given stake address (account) - /account_info_cached: #RPC + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (effective for performance query against registered accounts) + /account_utxos: #RPC post: tags: - Stake Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_extended" responses: "200": - description: Array of account information + description: Array of account UTxOs associated with given stake addresses content: application/json: schema: - $ref: "#/components/schemas/account_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account Information (Cached) - description: Get the cached account information for given stake addresses, effective for registered accounts + summary: UTxOs for stake addresses (accounts) + description: Get a list of all UTxOs for given stake addresses (account)s + /account_txs: #RPC + get: + tags: + - Stake Account + parameters: + - $ref: "#/components/parameters/_stake_address" + - $ref: "#/components/parameters/_after_block_height" + responses: + "200": + description: Array of Txs associated with stake address (account) + content: + application/json: + schema: + $ref: "#/components/schemas/address_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Txs + description: Get a list of all Txs for a given stake address (account) /account_rewards: #RPC post: tags: @@ -783,6 +896,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Account History description: Get the staking history of given stake addresses (accounts) + /asset_list: get: tags: @@ -802,95 +916,89 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) - /asset_token_registry: + /policy_asset_list: #RPC get: tags: - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" responses: "200": - description: Array of token registry information for each asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_token_registry" + $ref: "#/components/schemas/policy_asset_list" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Token Registry - description: Get a list of assets registered via token registry on github - /asset_addresses: #RPC + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) + /asset_token_registry: get: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of token registry information for each asset content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_token_registry" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Addresses - description: Get the list of all addresses holding a given asset

    - `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects - with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to - query layers to have a dedicated cache table for themselves served via Koios.` - /asset_address_list: #RPC - get: + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github + /asset_info: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + requestBody: + $ref: "#/components/requestBodies/asset_list" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of detailed asset information content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Address List [DEPRECATED] - description: Get the list of all addresses holding a given asset (replaced by asset_addresses) - /asset_nft_address: #RPC - get: + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata + /asset_utxos: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy_nft" - - $ref: "#/components/parameters/_asset_name_nft" + requestBody: + $ref: "#/components/requestBodies/asset_list_with_extended" responses: "200": - description: Payment addresses currently holding the given NFT + description: Array of UTXOs for given asset list content: application/json: schema: - $ref: "#/components/schemas/asset_nft_address" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: NFT Address - description: Get the address where specified NFT currently reside on. - /asset_info: #RPC + summary: Asset UTXOs + description: Get the UTXO information of a list of assets including + /asset_history: #RPC get: tags: - Asset @@ -899,61 +1007,66 @@ paths: - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of asset mint/burn history content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_history" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information - description: Get the information of an asset including first minting & token registry metadata - post: + summary: Asset History + description: Get the mint/burn history of an asset + /asset_addresses: #RPC + get: tags: - Asset - requestBody: - $ref: "#/components/requestBodies/asset_list" + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information (Bulk) - description: Get the information of a list of assets including first minting & token registry metadata - /asset_history: #RPC + summary: Asset Addresses + description: Get the list of all addresses holding a given asset

    + `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects + with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to + query layers to have a dedicated cache table for themselves served via Koios.` + /asset_nft_address: #RPC get: tags: - Asset parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" responses: "200": - description: Array of asset mint/burn history + description: Payment addresses currently holding the given NFT content: application/json: schema: - $ref: "#/components/schemas/asset_history" + $ref: "#/components/schemas/asset_nft_address" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset History - description: Get the mint/burn history of an asset + summary: NFT Address + description: Get the address where specified NFT currently reside on. /policy_asset_addresses: #RPC get: tags: @@ -999,94 +1112,98 @@ paths: $ref: "#/components/responses/NotFound" summary: Policy Asset Information description: Get the information for all assets under the same policy - /asset_policy_info: #RPC + /asset_summary: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed information of assets under the same policy + description: Array of asset summary information content: application/json: schema: - $ref: "#/components/schemas/policy_asset_info" + $ref: "#/components/schemas/asset_summary" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information [DEPRECATED] - description: Get the information for all assets under the same policy (replaced by asset_addresses) - /policy_asset_list: #RPC + summary: Asset Summary + description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) + /asset_txs: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": - description: Array of detailed information of assets under the same policy + description: An array of Tx hashes that included the given asset (latest first) content: application/json: schema: - $ref: "#/components/schemas/policy_asset_list" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Policy Asset List - description: Get the list of asset under the given policy (including balances) - /asset_summary: #RPC + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) + /asset_address_list: #RPC get: tags: - Asset + deprecated: true parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of asset summary information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_summary" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Summary - description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) - /asset_txs: #RPC + summary: Asset Address List + description: Get the list of all addresses holding a given asset [DEPRECATED - replaced by asset_addresses] + /asset_policy_info: #RPC get: + deprecated: true tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - - $ref: "#/components/parameters/_after_block_height" - - $ref: "#/components/parameters/_history" responses: "200": - description: Array of Tx hashes that included the given asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_txs" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transactions - description: Get the list of current or all asset transaction hashes (newest first) + summary: Asset Policy Information + description: Get the information for all assets under the same policy (DEPRECATED - replaced by policy_asset_info) + /pool_list: #RPC get: tags: @@ -1105,7 +1222,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool List - description: A list of all currently registered/retiring (not retired) pools + description: List of brief info for all pools /pool_info: #RPC post: tags: @@ -1261,6 +1378,48 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Updates (History) description: Return all pool updates for all pools or only updates for specific pool if specified + /pool_registrations: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Registrations + description: Return all pool registrations initiated in the requested epoch + /pool_retirements: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Retirements + description: Return all pool retirements initiated in the requested epoch /pool_relays: #RPC get: tags: @@ -1279,7 +1438,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Relays - description: A list of registered relays for all currently registered/retiring (not retired) pools + description: A list of registered relays for all pools /pool_metadata: #RPC post: tags: @@ -1300,7 +1459,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Metadata - description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools + description: Metadata (on & off-chain) for all pools + + /script_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/script_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/script_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes /native_script_list: #RPC get: tags: @@ -1311,7 +1492,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/native_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1330,7 +1511,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/plutus_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1360,6 +1541,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /script_utxos: #RPC + get: + tags: + - Script + parameters: + - $ref: "#/components/parameters/_script_hash" + - $ref: "#/components/parameters/_extended" + responses: + "200": + description: List of UTXOs for a given script hash + content: + application/json: + schema: + $ref: "#/components/schemas/utxo_infos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Script UTXOs + description: List of all UTXOs for a given script hash /datum_info: #RPC post: tags: @@ -1381,6 +1584,43 @@ paths: $ref: "#/components/responses/NotFound" summary: Datum Information description: List of datum information for given datum hashes + /ogmios/?EvaluateTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains EvaluateTransaction payload as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?EvaluateTransaction + responses: + "200": + description: OK + "400": + description: An error occured while submitting transaction. + summary: Evaluate Transaction + description: Evaluate execution units of scripts in a well-formed transaction. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?EvaluateTransaction) for complete spec + /ogmios/?SubmitTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains a CBOR-serialized signed transaction (base16-encoded) as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?SubmitTransaction + responses: + "200": + description: OK + "400": + description: An error occured while querying transaction. + summary: Submit Transaction + description: Submit a signed and serialized transaction to the network. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?SubmitTransaction) for complete spec + components: parameters: select: @@ -1554,6 +1794,16 @@ components: in: query required: false allowEmptyValue: true + _extended: + deprecated: false + name: _extended + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _history: deprecated: false name: _history @@ -1661,6 +1911,29 @@ components: _addresses: - addr_test1vzpwq95z3xyum8vqndgdd9mdnmafh3djcxnc6jemlgdmswcve6tkw - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc + payment_addresses_with_extended: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _addresses: + - addr_test1vzpwq95z3xyum8vqndgdd9mdnmafh3djcxnc6jemlgdmswcve6tkw + - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc + _extended: true address_txs: content: application/json: @@ -1733,6 +2006,32 @@ components: _stake_addresses: - stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l - stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx + _first_only: false + _empty: false + + stake_addresses_with_extended: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _stake_addresses: + - stake_test1urq4rcynzj4uxqc74c852zky7wa6epgmn9r6k3j3gv7502q8jks0l + - stake_test1ur4t5nhceyn2amfuj7z74uxmmj8jf9fmgd2egqw8c98ve3cp2g4wx + _extended: true stake_addresses: content: application/json: @@ -1788,10 +2087,15 @@ components: items: type: string description: Array of Cardano payment credential(s) in hex format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _payment_credentials: - b429738bd6cc58b5c7932d001aa2bd05cfea47020a556c8c753d4436 - 82e016828989cd9d809b50d6976d9efa9bc5b2c1a78d4b3bfa1bb83b + _extended: true tx_ids: content: application/json: @@ -1853,6 +2157,22 @@ components: - pool1ext7qrwjzaxcdfhdnkq5mth59ukuu2atcg6tgqpmevpt7ratkta - pool1x4p3cwemsm356vpxnjwuud7w76jz64hyss729zp7xa6wuey6yr9 - pool1ws42l6rawqjv58crs5l32v0eem3qnngpnjfd7epwd4lmjccc5cg + script_hashes: + content: + application/json: + schema: + type: object + properties: + _script_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano script hashes + example: + _script_hashes: + - a8e9f8f34fd631b1d5b9f41a90f4abc0d3935cea7baba0bb34c96f59 + - b4fd6dfe4a643aeec5d75dbb1f27198fc2aabf30bf92ed5470253792 datum_hashes: content: application/json: @@ -1866,10 +2186,30 @@ components: type: string description: Array of Cardano datum hashes example: - _datum_hashes: - - 5571e2c3549f15934a38382d1318707a86751fb70827f4cbd29b104480f1be9b - - 5f7212f546d7e7308ce99b925f05538db19981f4ea3084559c0b28a363245826 - asset_list: + _datum_hashes: + - 5571e2c3549f15934a38382d1318707a86751fb70827f4cbd29b104480f1be9b + - 5f7212f546d7e7308ce99b925f05538db19981f4ea3084559c0b28a363245826 + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e','7447454e53'] + - ['777e6b4903dab74963ae581d39875c5dac16c09bb1f511c0af1ddda8','6141414441'] + asset_list_with_extended: content: application/json: schema: @@ -1885,11 +2225,43 @@ components: type: array items: type: string + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _asset_list: - ['c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e','7447454e53'] - ['777e6b4903dab74963ae581d39875c5dac16c09bb1f511c0af1ddda8','6141414441'] - securitySchemes: {} + _extended: true + utxo_refs_with_extended: + content: + application/json: + schema: + required: + - _utxo_refs + type: object + properties: + _utxo_refs: + format: text + type: array + items: + type: string + description: Array of Cardano utxo references in the form "hash#index" + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _utxo_refs: + - d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530#0 + - 145688d3619e7524510ea64c0ec6363b77a9b8da179ef9bb0273a0940d57d576#0 + _extended: false + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT schemas: tip: type: array @@ -2003,20 +2375,59 @@ components: type: string description: JSON encoded data with details about the parameter update example: {"decentralisation": 0.9} + reserve_withdrawals: + type: array + items: + properties: + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" pool_list: type: array items: properties: pool_id_bech32: - type: string - nullable: true - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + pool_id_hex: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" + active_epoch_no: + $ref: "#/components/schemas/pool_info/items/properties/active_epoch_no" + margin: + $ref: "#/components/schemas/pool_info/items/properties/margin" + fixed_cost: + $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" + pledge: + $ref: "#/components/schemas/pool_info/items/properties/pledge" + reward_addr: + $ref: "#/components/schemas/pool_info/items/properties/reward_addr" + owners: + $ref: "#/components/schemas/pool_info/items/properties/margin" + relays: + $ref: "#/components/schemas/pool_info/items/properties/margin" ticker: type: string nullable: true description: Pool ticker - example: JAZZ + example: AHL + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/margin" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/margin" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/margin" + retiring_epoch: + $ref: "#/components/schemas/pool_info/items/properties/margin" pool_history_info: type: array items: @@ -2088,26 +2499,32 @@ components: $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string + nullable: true description: Pool VRF key hash example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 margin: type: number + nullable: true description: Margin (decimal format) example: 0.1 fixed_cost: type: string + nullable: true description: Pool fixed cost per epoch example: "500000000" pledge: type: string + nullable: true description: Pool pledge in lovelace example: "64000000000000" reward_addr: type: string + nullable: true description: Pool reward address example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 owners: type: array + nullable: true items: type: string description: Pool (co)owner address @@ -2270,6 +2687,26 @@ components: type: string description: Latest transaction hash used for delegation by the account example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_registrations: + type: array + nullable: true + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + active_epoch_no: + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" pool_delegators_history: type: array nullable: true @@ -2318,6 +2755,7 @@ components: active_epoch_no: type: integer description: Epoch number in which the update becomes active + nullable: true example: 324 vrf_key_hash: $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" @@ -2339,8 +2777,11 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" - pool_status: - $ref: "#/components/schemas/pool_info/items/properties/pool_status" + update_type: + type: string + description: Type of update task + enum: ["registration", "deregistration"] + example: registered retiring_epoch: $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" pool_relays: @@ -2352,6 +2793,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" relays: $ref: "#/components/schemas/pool_info/items/properties/relays" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" pool_metadata: type: array items: @@ -2365,6 +2808,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" epoch_info: type: array items: @@ -2533,8 +2978,8 @@ components: description: The hash of the first block where these parameters are valid example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 cost_models: - type: string - description: The per language cost models + type: object + description: The per language cost model in JSON example: null nullable: true price_mem: @@ -2724,17 +3169,21 @@ components: properties: block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" - tx_hashes: - type: array - items: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" address_info: type: array items: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2753,9 +3202,9 @@ components: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: @@ -2769,7 +3218,7 @@ components: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" address_txs: type: array items: @@ -2789,22 +3238,17 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" - asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" - credential_txs: - $ref: "#/components/schemas/address_txs" - credential_utxos: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" - tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" - value: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + $ref: "#/components/schemas/utxo_infos/items/properties/address" + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_list: type: array items: @@ -2825,7 +3269,9 @@ components: enum: ["registered", "not registered"] example: registered delegated_pool: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + nullable: true + allOf: + - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" total_balance: type: string description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) @@ -2854,23 +3300,67 @@ components: type: string description: Total treasury MIR value of the account example: "0" - account_utxos: + utxo_infos: type: array items: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + type: string + description: Hash identifier of the transaction + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + type: integer + description: Index of UTxO in the transaction + example: 0 address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + type: string + description: A Cardano payment/base address (bech32 encoded) + example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp value: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + stake_address: + $ref: "#/components/schemas/address_info/items/properties/stake_address" + payment_cred: + type: string + description: Payment credential + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 + nullable: true + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" + datum_hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" + asset_list: + type: array + nullable: true + description: An array of assets on the UTxO + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + type: string + description: Quantity of assets on the UTxO + example: 1 + is_spent: + type: boolean + description: True if the UTXO has been spent + example: true + account_rewards: type: array items: @@ -2915,7 +3405,7 @@ components: enum: ["registration", "delegation", "withdrawal", "deregistration"] example: "registration" tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" epoch_no: $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: @@ -2934,7 +3424,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" account_assets: type: array items: @@ -2942,25 +3432,16 @@ components: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - asset_list: - type: array - items: - type: object - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - type: integer - description: Asset decimals - example: 6 - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_history: type: array items: @@ -2992,9 +3473,7 @@ components: type: object properties: tx_hash: - type: string - description: Hash identifier of the transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" block_height: @@ -3061,25 +3540,17 @@ components: type: object properties: bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw + $ref: "#/components/schemas/utxo_infos/items/properties/address" cred: type: string description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3129,23 +3600,7 @@ components: description: Value (json) example: null asset_list: - type: array - nullable: true - description: An array of assets on the UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - quantity: - type: string - description: Quantity of assets on the UTxO - example: 1 + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -3174,7 +3629,7 @@ components: fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3212,7 +3667,7 @@ components: items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" script_json: type: object description: JSON representation of the timelock script (null for other script types) @@ -3237,6 +3692,7 @@ components: } plutus_contracts: type: array + nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -3246,15 +3702,11 @@ components: example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 nullable: true script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" bytecode: - type: string - description: CBOR-encoded Plutus script data - example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 + $ref: "#/components/schemas/script_info/items/properties/bytes" size: - type: integer - description: The size of the CBOR serialised script (in bytes) - example: 234895 + $ref: "#/components/schemas/script_info/items/properties/size" valid_contract: type: boolean description: True if the contract is valid or there is no contract @@ -3314,17 +3766,11 @@ components: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3339,7 +3785,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" metadata: type: object nullable: true @@ -3360,7 +3806,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -3414,9 +3860,7 @@ components: items: properties: payment_address: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: type: string description: Asset balance on the payment address @@ -3427,7 +3871,7 @@ components: items: properties: payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" asset_summary: type: array items: @@ -3519,6 +3963,11 @@ components: decimals: type: integer example: 0 + cip68_metadata: + type: object + description: CIP 68 metadata if present for asset + nullable: true + example: {"222": {"fields": [{"map": [{"k": {"bytes": "6e616d65"}, "v": {"bytes": "74657374"}}]}], "constructor": 0}} asset_history: type: array items: @@ -3559,7 +4008,7 @@ components: asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: $ref: "#/components/schemas/asset_addresses/items/properties/quantity" policy_asset_info: @@ -3599,67 +4048,65 @@ components: total_supply: $ref: "#/components/schemas/asset_info/items/properties/total_supply" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - asset_txs: - type: array - description: An array of Tx hashes that included the given asset (latest first) - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - native_script_list: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + script_info: type: array items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + type: string + description: Hash of a script + example: bfa7ffa9b2e164873db6ac6d0528c82e212963bc62e10fd1d81da4af creation_tx_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" + type: string + description: Hash of the script creation transaction + example: 255f061502ad83230351fbcf2d9fade1b5d118d332f92c9861075010a1fe3fbe type: type: string description: Type of the script - enum: ["timelock", "multisig"] - example: timelock - plutus_script_list: + enum: ["plutusV1","plutusV2","timelock","multisig"] + example: plutusV1 + value: + type: object + nullable: true + description: Data in JSON format + example: null + bytes: + type: string + description: Script bytes (cborSeq) + example: 5907f4010000332323232323232323233223232323232332232323232322223232533532533533355300712001323212330012233350052200200200100235001220011233001225335002101710010142325335333573466e3cd400488008d4020880080580544ccd5cd19b873500122001350082200101601510153500122002353500122002222222222200a101413357389201115554784f206e6f7420636f6e73756d6564000133333573466e1cd55cea8012400046644246600200600464646464646464646464646666ae68cdc39aab9d500a480008cccccccccc888888888848cccccccccc00402c02802402001c01801401000c008cd40508c8c8cccd5cd19b8735573aa0049000119910919800801801180f9aba150023019357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854028cd4050054d5d0a804999aa80bbae501635742a010666aa02eeb94058d5d0a80399a80a0109aba15006335014335502402275a6ae854014c8c8c8cccd5cd19b8735573aa00490001199109198008018011919191999ab9a3370e6aae754009200023322123300100300233502575a6ae854008c098d5d09aba2500223263533573805e05c05a05826aae7940044dd50009aba150023232323333573466e1cd55cea8012400046644246600200600466a04aeb4d5d0a80118131aba135744a004464c6a66ae700bc0b80b40b04d55cf280089baa001357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854010cd4051d71aba15003335014335502475c40026ae854008c070d5d09aba2500223263533573804e04c04a04826ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d400520062321222230040053019357426aae79400c8cccd5cd19b875002480108c848888c008014c06cd5d09aab9e500423333573466e1d400d20022321222230010053015357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6a66ae7008808408007c0780740704d55cea80089baa001357426ae8940088c98d4cd5ce00d80d00c80c080c89931a99ab9c4910350543500019018135573ca00226ea8004c8004d5405888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000448c88c008dd6000990009aa80b111999aab9f00125009233500830043574200460066ae880080548c8c8c8cccd5cd19b8735573aa00690001199911091998008020018011919191999ab9a3370e6aae7540092000233221233001003002301735742a00466a01c02c6ae84d5d1280111931a99ab9c01b01a019018135573ca00226ea8004d5d0a801999aa803bae500635742a00466a014eb8d5d09aba2500223263533573802e02c02a02826ae8940044d55cf280089baa0011335500175ceb44488c88c008dd5800990009aa80a11191999aab9f0022500823350073355017300635573aa004600a6aae794008c010d5d100180a09aba100111220021221223300100400312232323333573466e1d4005200023212230020033005357426aae79400c8cccd5cd19b8750024800884880048c98d4cd5ce00980900880800789aab9d500113754002464646666ae68cdc39aab9d5002480008cc8848cc00400c008c014d5d0a8011bad357426ae8940088c98d4cd5ce00800780700689aab9e5001137540024646666ae68cdc39aab9d5001480008dd71aba135573ca004464c6a66ae7003803403002c4dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6a66ae7004404003c0380340304d55cea80089baa0012323333573466e1d40052002200523333573466e1d40092000200523263533573801a01801601401226aae74dd5000891001091000919191919191999ab9a3370ea002900610911111100191999ab9a3370ea004900510911111100211999ab9a3370ea00690041199109111111198008048041bae35742a00a6eb4d5d09aba2500523333573466e1d40112006233221222222233002009008375c6ae85401cdd71aba135744a00e46666ae68cdc3a802a400846644244444446600c01201060186ae854024dd71aba135744a01246666ae68cdc3a8032400446424444444600e010601a6ae84d55cf280591999ab9a3370ea00e900011909111111180280418071aba135573ca018464c6a66ae7004c04804404003c03803403002c0284d55cea80209aab9e5003135573ca00426aae7940044dd50009191919191999ab9a3370ea002900111999110911998008028020019bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c8488c00800cc020d5d09aab9e500623263533573801801601401201026aae75400c4d5d1280089aab9e500113754002464646666ae68cdc3a800a400446424460020066eb8d5d09aab9e500323333573466e1d400920002321223002003375c6ae84d55cf280211931a99ab9c009008007006005135573aa00226ea800444888c8c8cccd5cd19b8735573aa0049000119aa80518031aba150023005357426ae8940088c98d4cd5ce00480400380309aab9e5001137540029309000a490350543100112212330010030021123230010012233003300200200133512233002489209366f09fe40eaaeb17d3cb6b0b61e087d664174c39a48a986f86b2b0ba6e2a7b00480008848cc00400c0088005 + size: + type: integer + description: The size of the CBOR serialised script (in bytes) + example: 2039 + script_list: type: array items: properties: script_hash: - type: string - description: Hash of a script - example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 + $ref: "#/components/schemas/script_info/items/properties/script_hash" creation_tx_hash: - type: string - description: Hash of the script creation transaction - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" + type: + $ref: "#/components/schemas/script_info/items/properties/type" + size: + $ref: "#/components/schemas/script_info/items/properties/size" script_redeemers: type: array items: type: object properties: script_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/script_info/items/properties/script_hash" redeemers: type: array items: type: object properties: tx_hash: - type: string - description: Hash of Transaction containing the redeemer - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: The index of the redeemer pointer in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" unit_mem: description: The budget in Memory to run a script example: 520448 @@ -3691,20 +4138,20 @@ components: nullable: true example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 datum_value: - type: object - description: The actual data in json format - example: { "bytes": "3c33" } + $ref: "#/components/schemas/script_info/items/properties/value" datum_info: type: array items: type: object properties: - hash: + datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + creation_tx_hash: + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + $ref: "#/components/schemas/script_info/items/properties/value" bytes: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" + $ref: "#/components/schemas/script_info/items/properties/bytes" headers: {} responses: OK: @@ -3712,7 +4159,7 @@ components: NotFound: description: The server does not recognise the combination of endpoint and parameters provided Unauthorized: - description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint + description: Access token is missing or invalid PartialContent: description: The result was truncated BadRequest: @@ -3730,12 +4177,12 @@ tags: - name: Transactions description: Query blockchain transaction details x-tag-expanded: false + - name: Stake Account + description: Query details about specific stake account addresses + x-tag-expanded: false - name: Address description: Query information about specific address(es) x-tag-expanded: false - - name: Account - description: Query details about specific stake account addresses - x-tag-expanded: false - name: Asset description: Query Asset related informations x-tag-expanded: false @@ -3745,4 +4192,5 @@ tags: - name: Script description: Query information about specific scripts (Smart Contracts) x-tag-expanded: false -security: [] +security: + - bearerAuth: [] diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index debc78c1..25db005a 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -1,7 +1,7 @@ openapi: 3.0.2 info: title: Koios API - version: 1.0.10 + version: v1.1.0rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. @@ -14,11 +14,11 @@ info: Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

    ``` bash - curl "https://api.koios.rest/api/v0/tip" + curl "https://api.koios.rest/api/v1/tip" # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] - curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" + curl "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_height" # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] ``` @@ -27,7 +27,7 @@ info: You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

    ``` bash - curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" + curl "https://api.koios.rest/api/v1/blocks?epoch=eq.250&epoch_slot=lt.180" # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] @@ -62,7 +62,7 @@ info: Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -I | grep -i content-range # content-range: 0-999/* @@ -71,7 +71,7 @@ info: As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range # content-range: 1000-1499/* @@ -80,7 +80,7 @@ info: For GET endpoints, there is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range # content-range: 1000-1499/* @@ -94,7 +94,7 @@ info: Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" + curl -s "https://api.koios.rest/api/v1/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, @@ -109,13 +109,13 @@ info: Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" # epoch,epoch_slot,block_time # 318,28491,1643607582 @@ -134,6 +134,10 @@ info: Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. + # Authentication + + While Koios public tier remains unauthenticated and allows queries without any authentication, it has low limits to prevent actions against an erroraneous query/loop from a consumer. There is also a Free tier which requires setting up Bearer Auth token that is linked to the owner's wallet account (which can be connected to via [Koios website](https://koios.rest/pricing/Pricing.html) ). + The examples across this API site already [supports authentication](/#auth), for you to use in the queries. # Community projects @@ -142,11 +146,12 @@ info: x-logo: url: "https://api.koios.rest/images/koios.png" servers: - - url: https://api.koios.rest/api/v0 - - url: https://guild.koios.rest/api/v0 - - url: https://preview.koios.rest/api/v0 - - url: https://preprod.koios.rest/api/v0 + - url: https://api.koios.rest/api/v1 + - url: https://guild.koios.rest/api/v1 + - url: https://preview.koios.rest/api/v1 + - url: https://preprod.koios.rest/api/v1 paths: + /tip: #RPC get: tags: @@ -227,6 +232,45 @@ paths: $ref: "#/components/responses/NotFound" summary: Param Update Proposals description: Get all parameter update proposals submitted to the chain starting Shelley era + /reserve_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from reserves against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Reserve Withdrawals + description: List of all withdrawals from reserves against stake accounts + /treasury_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from treasury against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Treasury Withdrawals + description: List of all withdrawals from treasury against stake accounts + /epoch_info: #RPC get: tags: @@ -294,6 +338,7 @@ paths: summary: Epoch's Block Protocols description: >- Get the information about block protocol distribution in epoch + /blocks: get: tags: @@ -355,28 +400,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Block Transactions description: Get a list of all transactions included in provided blocks - /tx_info: #RPC + + /utxo_info: #RPC post: tags: - Transactions requestBody: - $ref: "#/components/requestBodies/tx_ids" + $ref: "#/components/requestBodies/utxo_refs_with_extended" responses: "200": - description: Array of detailed information about transaction(s) + description: Array of UTXO details content: application/json: schema: - $ref: "#/components/schemas/tx_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Information - description: Get detailed information about transaction(s) - /tx_utxos: #RPC + summary: UTxO Info + description: Get UTxO set for requested UTxO references + /tx_info: #RPC post: tags: - Transactions @@ -384,19 +430,19 @@ paths: $ref: "#/components/requestBodies/tx_ids" responses: "200": - description: Array of inputs and outputs for given transaction(s) + description: Array of detailed information about transaction(s) content: application/json: schema: - $ref: "#/components/schemas/tx_utxos" + $ref: "#/components/schemas/tx_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction UTxOs [DEPRECATED] - description: Get UTxO set (inputs/outputs) of transactions. + summary: Transaction Information + description: Get detailed information about transaction(s) /tx_metadata: #RPC post: tags: @@ -450,7 +496,7 @@ paths: # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. curl -X POST \ --header "Content-Type: application/cbor" \ - --data-binary ${data} https://api.koios.rest/api/v0/submittx + --data-binary @${data} https://api.koios.rest/api/v1/submittx responses: "202": description: OK @@ -486,8 +532,31 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Status (Block Confirmations) + summary: Transaction Status description: Get the number of block confirmations for a given transaction hash list + /tx_utxos: #RPC + post: + tags: + - Transactions + deprecated: true + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of inputs and outputs for given transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_utxos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction UTxOs + description: Get UTxO set (inputs/outputs) of transactions [DEPRECATED - Use /utxo_info instead]. + /address_info: #RPC post: tags: @@ -509,27 +578,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Address Information description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses - /address_txs: #RPC + /address_utxos: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/address_txs" + $ref: "#/components/requestBodies/payment_addresses_with_extended" responses: "200": - description: Array of transaction hashes + description: Array of address UTXOs content: application/json: schema: - $ref: "#/components/schemas/address_txs" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Transactions - description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) + summary: Address UTXOs + description: Get UTxO set for given addresses /credential_utxos: #RPC post: tags: @@ -542,7 +611,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_utxos" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": @@ -550,28 +619,28 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: UTxOs from payment credentials - description: Get a list of UTxO against input payment credential array including their balances - /address_assets: #RPC + description: Get UTxO details for requested payment credentials + /address_txs: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/payment_addresses" + $ref: "#/components/requestBodies/address_txs" responses: "200": - description: Array of address-owned assets + description: Array of transaction hashes content: application/json: schema: - $ref: "#/components/schemas/address_assets" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for given addresses + summary: Address Transactions + description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /credential_txs: #RPC post: tags: @@ -584,7 +653,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_txs" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": @@ -593,6 +662,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Transactions from payment credentials description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) + /address_assets: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address-owned assets + content: + application/json: + schema: + $ref: "#/components/schemas/address_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Assets + description: Get the list of all the assets (policy, name and quantity) for given addresses + /account_list: get: tags: @@ -633,48 +724,70 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses - /account_utxos: #RPC - get: + /account_info_cached: #RPC + post: tags: - Stake Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses" responses: "200": - description: Array of account UTxOs associated with stake address + description: Array of account information content: application/json: schema: - $ref: "#/components/schemas/account_utxos" + $ref: "#/components/schemas/account_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account UTxOs - description: Get a list of all UTxOs for a given stake address (account) - /account_info_cached: #RPC + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (effective for performance query against registered accounts) + /account_utxos: #RPC post: tags: - Stake Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_extended" responses: "200": - description: Array of account information + description: Array of account UTxOs associated with given stake addresses content: application/json: schema: - $ref: "#/components/schemas/account_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account Information (Cached) - description: Get the cached account information for given stake addresses, effective for registered accounts + summary: UTxOs for stake addresses (accounts) + description: Get a list of all UTxOs for given stake addresses (account)s + /account_txs: #RPC + get: + tags: + - Stake Account + parameters: + - $ref: "#/components/parameters/_stake_address" + - $ref: "#/components/parameters/_after_block_height" + responses: + "200": + description: Array of Txs associated with stake address (account) + content: + application/json: + schema: + $ref: "#/components/schemas/address_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Txs + description: Get a list of all Txs for a given stake address (account) /account_rewards: #RPC post: tags: @@ -783,6 +896,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Account History description: Get the staking history of given stake addresses (accounts) + /asset_list: get: tags: @@ -802,95 +916,89 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) - /asset_token_registry: + /policy_asset_list: #RPC get: tags: - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" responses: "200": - description: Array of token registry information for each asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_token_registry" + $ref: "#/components/schemas/policy_asset_list" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Token Registry - description: Get a list of assets registered via token registry on github - /asset_addresses: #RPC + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) + /asset_token_registry: get: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of token registry information for each asset content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_token_registry" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Addresses - description: Get the list of all addresses holding a given asset

    - `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects - with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to - query layers to have a dedicated cache table for themselves served via Koios.` - /asset_address_list: #RPC - get: + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github + /asset_info: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + requestBody: + $ref: "#/components/requestBodies/asset_list" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of detailed asset information content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Address List [DEPRECATED] - description: Get the list of all addresses holding a given asset (replaced by asset_addresses) - /asset_nft_address: #RPC - get: + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata + /asset_utxos: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy_nft" - - $ref: "#/components/parameters/_asset_name_nft" + requestBody: + $ref: "#/components/requestBodies/asset_list_with_extended" responses: "200": - description: Payment addresses currently holding the given NFT + description: Array of UTXOs for given asset list content: application/json: schema: - $ref: "#/components/schemas/asset_nft_address" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: NFT Address - description: Get the address where specified NFT currently reside on. - /asset_info: #RPC + summary: Asset UTXOs + description: Get the UTXO information of a list of assets including + /asset_history: #RPC get: tags: - Asset @@ -899,61 +1007,66 @@ paths: - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of asset mint/burn history content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_history" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information - description: Get the information of an asset including first minting & token registry metadata - post: + summary: Asset History + description: Get the mint/burn history of an asset + /asset_addresses: #RPC + get: tags: - Asset - requestBody: - $ref: "#/components/requestBodies/asset_list" + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information (Bulk) - description: Get the information of a list of assets including first minting & token registry metadata - /asset_history: #RPC + summary: Asset Addresses + description: Get the list of all addresses holding a given asset

    + `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects + with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to + query layers to have a dedicated cache table for themselves served via Koios.` + /asset_nft_address: #RPC get: tags: - Asset parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" responses: "200": - description: Array of asset mint/burn history + description: Payment addresses currently holding the given NFT content: application/json: schema: - $ref: "#/components/schemas/asset_history" + $ref: "#/components/schemas/asset_nft_address" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset History - description: Get the mint/burn history of an asset + summary: NFT Address + description: Get the address where specified NFT currently reside on. /policy_asset_addresses: #RPC get: tags: @@ -999,94 +1112,98 @@ paths: $ref: "#/components/responses/NotFound" summary: Policy Asset Information description: Get the information for all assets under the same policy - /asset_policy_info: #RPC + /asset_summary: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed information of assets under the same policy + description: Array of asset summary information content: application/json: schema: - $ref: "#/components/schemas/policy_asset_info" + $ref: "#/components/schemas/asset_summary" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information [DEPRECATED] - description: Get the information for all assets under the same policy (replaced by asset_addresses) - /policy_asset_list: #RPC + summary: Asset Summary + description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) + /asset_txs: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": - description: Array of detailed information of assets under the same policy + description: An array of Tx hashes that included the given asset (latest first) content: application/json: schema: - $ref: "#/components/schemas/policy_asset_list" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Policy Asset List - description: Get the list of asset under the given policy (including balances) - /asset_summary: #RPC + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) + /asset_address_list: #RPC get: tags: - Asset + deprecated: true parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of asset summary information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_summary" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Summary - description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) - /asset_txs: #RPC + summary: Asset Address List + description: Get the list of all addresses holding a given asset [DEPRECATED - replaced by asset_addresses] + /asset_policy_info: #RPC get: + deprecated: true tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - - $ref: "#/components/parameters/_after_block_height" - - $ref: "#/components/parameters/_history" responses: "200": - description: Array of Tx hashes that included the given asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_txs" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transactions - description: Get the list of current or all asset transaction hashes (newest first) + summary: Asset Policy Information + description: Get the information for all assets under the same policy (DEPRECATED - replaced by policy_asset_info) + /pool_list: #RPC get: tags: @@ -1105,7 +1222,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool List - description: A list of all currently registered/retiring (not retired) pools + description: List of brief info for all pools /pool_info: #RPC post: tags: @@ -1261,6 +1378,48 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Updates (History) description: Return all pool updates for all pools or only updates for specific pool if specified + /pool_registrations: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Registrations + description: Return all pool registrations initiated in the requested epoch + /pool_retirements: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Retirements + description: Return all pool retirements initiated in the requested epoch /pool_relays: #RPC get: tags: @@ -1279,7 +1438,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Relays - description: A list of registered relays for all currently registered/retiring (not retired) pools + description: A list of registered relays for all pools /pool_metadata: #RPC post: tags: @@ -1300,7 +1459,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Metadata - description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools + description: Metadata (on & off-chain) for all pools + + /script_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/script_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/script_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes /native_script_list: #RPC get: tags: @@ -1311,7 +1492,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/native_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1330,7 +1511,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/plutus_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1360,6 +1541,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /script_utxos: #RPC + get: + tags: + - Script + parameters: + - $ref: "#/components/parameters/_script_hash" + - $ref: "#/components/parameters/_extended" + responses: + "200": + description: List of UTXOs for a given script hash + content: + application/json: + schema: + $ref: "#/components/schemas/utxo_infos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Script UTXOs + description: List of all UTXOs for a given script hash /datum_info: #RPC post: tags: @@ -1381,6 +1584,43 @@ paths: $ref: "#/components/responses/NotFound" summary: Datum Information description: List of datum information for given datum hashes + /ogmios/?EvaluateTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains EvaluateTransaction payload as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?EvaluateTransaction + responses: + "200": + description: OK + "400": + description: An error occured while submitting transaction. + summary: Evaluate Transaction + description: Evaluate execution units of scripts in a well-formed transaction. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?EvaluateTransaction) for complete spec + /ogmios/?SubmitTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains a CBOR-serialized signed transaction (base16-encoded) as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?SubmitTransaction + responses: + "200": + description: OK + "400": + description: An error occured while querying transaction. + summary: Submit Transaction + description: Submit a signed and serialized transaction to the network. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?SubmitTransaction) for complete spec + components: parameters: select: @@ -1554,6 +1794,16 @@ components: in: query required: false allowEmptyValue: true + _extended: + deprecated: false + name: _extended + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _history: deprecated: false name: _history @@ -1661,6 +1911,29 @@ components: _addresses: - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc - addr_test1vqneq3v0dqh3x3muv6ee3lt8e5729xymnxuavx6tndcjc2cv24ef9 + payment_addresses_with_extended: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _addresses: + - addr_test1vpfwv0ezc5g8a4mkku8hhy3y3vp92t7s3ul8g778g5yegsgalc6gc + - addr_test1vqneq3v0dqh3x3muv6ee3lt8e5729xymnxuavx6tndcjc2cv24ef9 + _extended: true address_txs: content: application/json: @@ -1733,6 +2006,32 @@ components: _stake_addresses: - stake_test1urqntq4wexjylnrdnp97qq79qkxxvrsa9lcnwr7ckjd6w0cr04y4p - stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl + _first_only: false + _empty: false + + stake_addresses_with_extended: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _stake_addresses: + - stake_test1urqntq4wexjylnrdnp97qq79qkxxvrsa9lcnwr7ckjd6w0cr04y4p + - stake_test1up6wqzrw2h9vvjy5zfkjn0dwtymy5r29zyhf8fyhm6fat9c2am5hl + _extended: true stake_addresses: content: application/json: @@ -1788,10 +2087,15 @@ components: items: type: string description: Array of Cardano payment credential(s) in hex format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _payment_credentials: - 33c378cee41b2e15ac848f7f6f1d2f78155ab12d93b713de898d855f - 52e63f22c5107ed776b70f7b92248b02552fd08f3e747bc745099441 + _extended: true tx_ids: content: application/json: @@ -1853,6 +2157,22 @@ components: - pool1p90428kec03mjdya3k4gv5d20w7lmed7ca0snknef5j977l3y8l - pool1wwh3k3ldzujdvgxllfwlnnkxyheafkacqlufnvpr77n5q72f9hw - pool1p835jxsj8py5n34lrgk6fvpgpxxvh585qm8dzvp7ups37vdet5a + script_hashes: + content: + application/json: + schema: + type: object + properties: + _script_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano script hashes + example: + _script_hashes: + - c6d963e8892916ab8753d3c342037cd122123c4dd783a07af21f8dac + - c0c671fba483641a71bb92d3a8b7c52c90bf1c01e2b83116ad7d4536 datum_hashes: content: application/json: @@ -1866,10 +2186,30 @@ components: type: string description: Array of Cardano datum hashes example: - _datum_hashes: - - 6181b3dc623cd8812caf027a3507e9b3095388a7cf3db65983e1fddd3a84c88c - - f8ae55ff89e1f5366f23e16bcaf2073252337b96031a02d79e41d653b5f0a978 - asset_list: + _datum_hashes: + - 6181b3dc623cd8812caf027a3507e9b3095388a7cf3db65983e1fddd3a84c88c + - f8ae55ff89e1f5366f23e16bcaf2073252337b96031a02d79e41d653b5f0a978 + asset_list: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + example: + _asset_list: + - ['065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404','433374'] + - ['189e2c53985411addb8df0f3e09f70e343da69f06746c408aba672a8','15fc257714a51769e192761d674db2ee2e80137428e522f9b914debb5f785301'] + asset_list_with_extended: content: application/json: schema: @@ -1885,11 +2225,43 @@ components: type: array items: type: string + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _asset_list: - ['065270479316f1d92e00f7f9f095ebeaac9d009c878dc35ce36d3404','433374'] - ['189e2c53985411addb8df0f3e09f70e343da69f06746c408aba672a8','15fc257714a51769e192761d674db2ee2e80137428e522f9b914debb5f785301'] - securitySchemes: {} + _extended: true + utxo_refs_with_extended: + content: + application/json: + schema: + required: + - _utxo_refs + type: object + properties: + _utxo_refs: + format: text + type: array + items: + type: string + description: Array of Cardano utxo references in the form "hash#index" + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _utxo_refs: + - 206f6da5b0b0de45605a95f5ce7e172be9674550f7dde3838c45cbf24bab8b00#0 + - f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c#0 + _extended: false + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT schemas: tip: type: array @@ -2003,20 +2375,59 @@ components: type: string description: JSON encoded data with details about the parameter update example: {"decentralisation": 0.9} + reserve_withdrawals: + type: array + items: + properties: + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" pool_list: type: array items: properties: pool_id_bech32: - type: string - nullable: true - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + pool_id_hex: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" + active_epoch_no: + $ref: "#/components/schemas/pool_info/items/properties/active_epoch_no" + margin: + $ref: "#/components/schemas/pool_info/items/properties/margin" + fixed_cost: + $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" + pledge: + $ref: "#/components/schemas/pool_info/items/properties/pledge" + reward_addr: + $ref: "#/components/schemas/pool_info/items/properties/reward_addr" + owners: + $ref: "#/components/schemas/pool_info/items/properties/margin" + relays: + $ref: "#/components/schemas/pool_info/items/properties/margin" ticker: type: string nullable: true description: Pool ticker - example: JAZZ + example: AHL + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/margin" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/margin" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/margin" + retiring_epoch: + $ref: "#/components/schemas/pool_info/items/properties/margin" pool_history_info: type: array items: @@ -2088,26 +2499,32 @@ components: $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string + nullable: true description: Pool VRF key hash example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 margin: type: number + nullable: true description: Margin (decimal format) example: 0.1 fixed_cost: type: string + nullable: true description: Pool fixed cost per epoch example: "500000000" pledge: type: string + nullable: true description: Pool pledge in lovelace example: "64000000000000" reward_addr: type: string + nullable: true description: Pool reward address example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 owners: type: array + nullable: true items: type: string description: Pool (co)owner address @@ -2270,6 +2687,26 @@ components: type: string description: Latest transaction hash used for delegation by the account example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_registrations: + type: array + nullable: true + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + active_epoch_no: + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" pool_delegators_history: type: array nullable: true @@ -2318,6 +2755,7 @@ components: active_epoch_no: type: integer description: Epoch number in which the update becomes active + nullable: true example: 324 vrf_key_hash: $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" @@ -2339,8 +2777,11 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" - pool_status: - $ref: "#/components/schemas/pool_info/items/properties/pool_status" + update_type: + type: string + description: Type of update task + enum: ["registration", "deregistration"] + example: registered retiring_epoch: $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" pool_relays: @@ -2352,6 +2793,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" relays: $ref: "#/components/schemas/pool_info/items/properties/relays" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" pool_metadata: type: array items: @@ -2365,6 +2808,8 @@ components: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" epoch_info: type: array items: @@ -2533,8 +2978,8 @@ components: description: The hash of the first block where these parameters are valid example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 cost_models: - type: string - description: The per language cost models + type: object + description: The per language cost model in JSON example: null nullable: true price_mem: @@ -2724,17 +3169,21 @@ components: properties: block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" - tx_hashes: - type: array - items: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" address_info: type: array items: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" balance: type: string description: Sum of all UTxO values beloning to address @@ -2753,9 +3202,9 @@ components: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: @@ -2769,7 +3218,7 @@ components: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" address_txs: type: array items: @@ -2789,22 +3238,17 @@ components: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" - asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" - credential_txs: - $ref: "#/components/schemas/address_txs" - credential_utxos: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" - tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" - value: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + $ref: "#/components/schemas/utxo_infos/items/properties/address" + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_list: type: array items: @@ -2825,7 +3269,9 @@ components: enum: ["registered", "not registered"] example: registered delegated_pool: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + nullable: true + allOf: + - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" total_balance: type: string description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) @@ -2854,23 +3300,67 @@ components: type: string description: Total treasury MIR value of the account example: "0" - account_utxos: + utxo_infos: type: array items: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + type: string + description: Hash identifier of the transaction + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + type: integer + description: Index of UTxO in the transaction + example: 0 address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + type: string + description: A Cardano payment/base address (bech32 encoded) + example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp value: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + stake_address: + $ref: "#/components/schemas/address_info/items/properties/stake_address" + payment_cred: + type: string + description: Payment credential + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 + nullable: true + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" + datum_hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" + asset_list: + type: array + nullable: true + description: An array of assets on the UTxO + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + type: string + description: Quantity of assets on the UTxO + example: 1 + is_spent: + type: boolean + description: True if the UTXO has been spent + example: true + account_rewards: type: array items: @@ -2915,7 +3405,7 @@ components: enum: ["registration", "delegation", "withdrawal", "deregistration"] example: "registration" tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" epoch_no: $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: @@ -2934,7 +3424,7 @@ components: addresses: type: array items: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" account_assets: type: array items: @@ -2942,25 +3432,16 @@ components: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - asset_list: - type: array - items: - type: object - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - type: integer - description: Asset decimals - example: 6 - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_history: type: array items: @@ -2992,9 +3473,7 @@ components: type: object properties: tx_hash: - type: string - description: Hash identifier of the transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" block_height: @@ -3061,25 +3540,17 @@ components: type: object properties: bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw + $ref: "#/components/schemas/utxo_infos/items/properties/address" cred: type: string description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3129,23 +3600,7 @@ components: description: Value (json) example: null asset_list: - type: array - nullable: true - description: An array of assets on the UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - quantity: - type: string - description: Quantity of assets on the UTxO - example: 1 + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -3174,7 +3629,7 @@ components: fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -3212,7 +3667,7 @@ components: items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" script_json: type: object description: JSON representation of the timelock script (null for other script types) @@ -3237,6 +3692,7 @@ components: } plutus_contracts: type: array + nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -3246,15 +3702,11 @@ components: example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 nullable: true script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" bytecode: - type: string - description: CBOR-encoded Plutus script data - example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 + $ref: "#/components/schemas/script_info/items/properties/bytes" size: - type: integer - description: The size of the CBOR serialised script (in bytes) - example: 234895 + $ref: "#/components/schemas/script_info/items/properties/size" valid_contract: type: boolean description: True if the contract is valid or there is no contract @@ -3314,17 +3766,11 @@ components: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -3339,7 +3785,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" metadata: type: object nullable: true @@ -3360,7 +3806,7 @@ components: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -3414,9 +3860,7 @@ components: items: properties: payment_address: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: type: string description: Asset balance on the payment address @@ -3427,7 +3871,7 @@ components: items: properties: payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" asset_summary: type: array items: @@ -3519,6 +3963,11 @@ components: decimals: type: integer example: 0 + cip68_metadata: + type: object + description: CIP 68 metadata if present for asset + nullable: true + example: {"222": {"fields": [{"map": [{"k": {"bytes": "6e616d65"}, "v": {"bytes": "74657374"}}]}], "constructor": 0}} asset_history: type: array items: @@ -3559,7 +4008,7 @@ components: asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: $ref: "#/components/schemas/asset_addresses/items/properties/quantity" policy_asset_info: @@ -3599,67 +4048,65 @@ components: total_supply: $ref: "#/components/schemas/asset_info/items/properties/total_supply" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - asset_txs: - type: array - description: An array of Tx hashes that included the given asset (latest first) - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - native_script_list: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + script_info: type: array items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + type: string + description: Hash of a script + example: bfa7ffa9b2e164873db6ac6d0528c82e212963bc62e10fd1d81da4af creation_tx_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" + type: string + description: Hash of the script creation transaction + example: 255f061502ad83230351fbcf2d9fade1b5d118d332f92c9861075010a1fe3fbe type: type: string description: Type of the script - enum: ["timelock", "multisig"] - example: timelock - plutus_script_list: + enum: ["plutusV1","plutusV2","timelock","multisig"] + example: plutusV1 + value: + type: object + nullable: true + description: Data in JSON format + example: null + bytes: + type: string + description: Script bytes (cborSeq) + example: 5907f4010000332323232323232323233223232323232332232323232322223232533532533533355300712001323212330012233350052200200200100235001220011233001225335002101710010142325335333573466e3cd400488008d4020880080580544ccd5cd19b873500122001350082200101601510153500122002353500122002222222222200a101413357389201115554784f206e6f7420636f6e73756d6564000133333573466e1cd55cea8012400046644246600200600464646464646464646464646666ae68cdc39aab9d500a480008cccccccccc888888888848cccccccccc00402c02802402001c01801401000c008cd40508c8c8cccd5cd19b8735573aa0049000119910919800801801180f9aba150023019357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854028cd4050054d5d0a804999aa80bbae501635742a010666aa02eeb94058d5d0a80399a80a0109aba15006335014335502402275a6ae854014c8c8c8cccd5cd19b8735573aa00490001199109198008018011919191999ab9a3370e6aae754009200023322123300100300233502575a6ae854008c098d5d09aba2500223263533573805e05c05a05826aae7940044dd50009aba150023232323333573466e1cd55cea8012400046644246600200600466a04aeb4d5d0a80118131aba135744a004464c6a66ae700bc0b80b40b04d55cf280089baa001357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854010cd4051d71aba15003335014335502475c40026ae854008c070d5d09aba2500223263533573804e04c04a04826ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d400520062321222230040053019357426aae79400c8cccd5cd19b875002480108c848888c008014c06cd5d09aab9e500423333573466e1d400d20022321222230010053015357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6a66ae7008808408007c0780740704d55cea80089baa001357426ae8940088c98d4cd5ce00d80d00c80c080c89931a99ab9c4910350543500019018135573ca00226ea8004c8004d5405888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000448c88c008dd6000990009aa80b111999aab9f00125009233500830043574200460066ae880080548c8c8c8cccd5cd19b8735573aa00690001199911091998008020018011919191999ab9a3370e6aae7540092000233221233001003002301735742a00466a01c02c6ae84d5d1280111931a99ab9c01b01a019018135573ca00226ea8004d5d0a801999aa803bae500635742a00466a014eb8d5d09aba2500223263533573802e02c02a02826ae8940044d55cf280089baa0011335500175ceb44488c88c008dd5800990009aa80a11191999aab9f0022500823350073355017300635573aa004600a6aae794008c010d5d100180a09aba100111220021221223300100400312232323333573466e1d4005200023212230020033005357426aae79400c8cccd5cd19b8750024800884880048c98d4cd5ce00980900880800789aab9d500113754002464646666ae68cdc39aab9d5002480008cc8848cc00400c008c014d5d0a8011bad357426ae8940088c98d4cd5ce00800780700689aab9e5001137540024646666ae68cdc39aab9d5001480008dd71aba135573ca004464c6a66ae7003803403002c4dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6a66ae7004404003c0380340304d55cea80089baa0012323333573466e1d40052002200523333573466e1d40092000200523263533573801a01801601401226aae74dd5000891001091000919191919191999ab9a3370ea002900610911111100191999ab9a3370ea004900510911111100211999ab9a3370ea00690041199109111111198008048041bae35742a00a6eb4d5d09aba2500523333573466e1d40112006233221222222233002009008375c6ae85401cdd71aba135744a00e46666ae68cdc3a802a400846644244444446600c01201060186ae854024dd71aba135744a01246666ae68cdc3a8032400446424444444600e010601a6ae84d55cf280591999ab9a3370ea00e900011909111111180280418071aba135573ca018464c6a66ae7004c04804404003c03803403002c0284d55cea80209aab9e5003135573ca00426aae7940044dd50009191919191999ab9a3370ea002900111999110911998008028020019bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c8488c00800cc020d5d09aab9e500623263533573801801601401201026aae75400c4d5d1280089aab9e500113754002464646666ae68cdc3a800a400446424460020066eb8d5d09aab9e500323333573466e1d400920002321223002003375c6ae84d55cf280211931a99ab9c009008007006005135573aa00226ea800444888c8c8cccd5cd19b8735573aa0049000119aa80518031aba150023005357426ae8940088c98d4cd5ce00480400380309aab9e5001137540029309000a490350543100112212330010030021123230010012233003300200200133512233002489209366f09fe40eaaeb17d3cb6b0b61e087d664174c39a48a986f86b2b0ba6e2a7b00480008848cc00400c0088005 + size: + type: integer + description: The size of the CBOR serialised script (in bytes) + example: 2039 + script_list: type: array items: properties: script_hash: - type: string - description: Hash of a script - example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 + $ref: "#/components/schemas/script_info/items/properties/script_hash" creation_tx_hash: - type: string - description: Hash of the script creation transaction - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" + type: + $ref: "#/components/schemas/script_info/items/properties/type" + size: + $ref: "#/components/schemas/script_info/items/properties/size" script_redeemers: type: array items: type: object properties: script_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/script_info/items/properties/script_hash" redeemers: type: array items: type: object properties: tx_hash: - type: string - description: Hash of Transaction containing the redeemer - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: The index of the redeemer pointer in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" unit_mem: description: The budget in Memory to run a script example: 520448 @@ -3691,20 +4138,20 @@ components: nullable: true example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 datum_value: - type: object - description: The actual data in json format - example: { "bytes": "3c33" } + $ref: "#/components/schemas/script_info/items/properties/value" datum_info: type: array items: type: object properties: - hash: + datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + creation_tx_hash: + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + $ref: "#/components/schemas/script_info/items/properties/value" bytes: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" + $ref: "#/components/schemas/script_info/items/properties/bytes" headers: {} responses: OK: @@ -3712,7 +4159,7 @@ components: NotFound: description: The server does not recognise the combination of endpoint and parameters provided Unauthorized: - description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint + description: Access token is missing or invalid PartialContent: description: The result was truncated BadRequest: @@ -3730,12 +4177,12 @@ tags: - name: Transactions description: Query blockchain transaction details x-tag-expanded: false + - name: Stake Account + description: Query details about specific stake account addresses + x-tag-expanded: false - name: Address description: Query information about specific address(es) x-tag-expanded: false - - name: Account - description: Query details about specific stake account addresses - x-tag-expanded: false - name: Asset description: Query Asset related informations x-tag-expanded: false @@ -3745,4 +4192,5 @@ tags: - name: Script description: Query information about specific scripts (Smart Contracts) x-tag-expanded: false -security: [] +security: + - bearerAuth: [] diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 656eaf17..da7e3265 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -1,6 +1,6 @@ info: title: Koios API - version: 1.0.10 + version: v1.1.0rc description: | Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. @@ -13,11 +13,11 @@ info: Instead of returning entire row, you can elect which rows you would like to fetch from the endpoint by using the `select` parameter with corresponding columns separated by commas. See example below (first is complete information for tip, while second command gives us 3 columns we are interested in):

    ``` bash - curl "https://api.koios.rest/api/v0/tip" + curl "https://api.koios.rest/api/v1/tip" # [{"hash":"4d44c8a453e677f933c3df42ebcf2fe45987c41268b9cfc9b42ae305e8c3d99a","epoch":317,"abs_slot":51700871,"epoch_slot":120071,"block_height":6806994,"block_time":1643267162}] - curl "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_height" + curl "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_height" # [{"epoch":317,"epoch_slot":120071,"block_height":6806994}] ``` @@ -26,7 +26,7 @@ info: You can filter the returned output based on specific conditions using operators against a column within returned result. Consider an example where you would want to query blocks minted in first 3 minutes of epoch 250 (i.e. epoch_slot was less than 180). To do so your query would look like below:

    ``` bash - curl "https://api.koios.rest/api/v0/blocks?epoch=eq.250&epoch_slot=lt.180" + curl "https://api.koios.rest/api/v1/blocks?epoch=eq.250&epoch_slot=lt.180" # [{"hash":"8fad2808ac6b37064a0fa69f6fe065807703d5235a57442647bbcdba1c02faf8","epoch":250,"abs_slot":22636942,"epoch_slot":142,"block_height":5385757,"block_time":1614203233,"tx_count":65,"vrf_key":"vrf_vk14y9pjprzlsjvjt66mv5u7w7292sxp3kn4ewhss45ayjga5vurgaqhqknuu","pool":null,"op_cert_counter":2}, # {"hash":"9d33b02badaedc0dedd0d59f3e0411e5fb4ac94217fb5ee86719e8463c570e16","epoch":250,"abs_slot":22636800,"epoch_slot":0,"block_height":5385756,"block_time":1614203091,"tx_count":10,"vrf_key":"vrf_vk1dkfsejw3h2k7tnguwrauqfwnxa7wj3nkp3yw2yw3400c4nlkluwqzwvka6","pool":null,"op_cert_counter":2}] @@ -61,7 +61,7 @@ info: Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -I | grep -i content-range # content-range: 0-999/* @@ -70,7 +70,7 @@ info: As we can see above, the number of observations returned was 1000 (range being 0-999), but the total size was not queried to avoid wait times. Now, let's modify this default behaviour to query rows beyond the first 999, but this time - also add another clause to limit results by 500. We can do this using `offset=1000` and `limit=500` as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height&offset=1000&limit=500" -I | grep -i content-range # content-range: 1000-1499/* @@ -79,7 +79,7 @@ info: For GET endpoints, there is also another method to achieve the above, instead of adding parameters to the URL itself, you can specify a `Range` header as below to achieve something similar:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range + curl -s "https://api.koios.rest/api/v1/blocks?select=block_height" -H "Range: 1000-1499" -I | grep -i content-range # content-range: 1000-1499/* @@ -93,7 +93,7 @@ info: Consider example where you want to check `epoch` and `epoch_slot` for the first 5 blocks created by a particular pool, i.e. you can set order to ascending based on block_height column and add horizontal filter for that pool ID as below:

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" + curl -s "https://api.koios.rest/api/v1/blocks?pool=eq.pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc&order=block_height.asc&limit=5" # [{"hash":"610b4c7bbebeeb212bd002885048cc33154ba29f39919d62a3d96de05d315706","epoch":236,"abs_slot":16594295,"epoch_slot":5495,"block_height":5086774,"block_time":1608160586,"tx_count":1,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, # {"hash":"d93d1db5275329ab695d30c06a35124038d8d9af64fc2b0aa082b8aa43da4164","epoch":236,"abs_slot":16597729,"epoch_slot":8929,"block_height":5086944,"block_time":1608164020,"tx_count":7,"vrf_key":"vrf_vk18x0e7dx8j37gdxftnn8ru6jcxs7n6acdazc4ykeda2ygjwg9a7ls7ns699","pool":"pool155efqn9xpcf73pphkk88cmlkdwx4ulkg606tne970qswczg3asc","op_cert_counter":1}, @@ -108,13 +108,13 @@ info: Below is an example of JSON/CSV output making use of above to print first in JSON (default), and then override response format to CSV.

    ``` bash - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" # [{"epoch":318,"epoch_slot":27867,"block_time":1643606958}, # {"epoch":318,"epoch_slot":27841,"block_time":1643606932}, # {"epoch":318,"epoch_slot":27839,"block_time":1643606930}] - curl -s "https://api.koios.rest/api/v0/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" + curl -s "https://api.koios.rest/api/v1/blocks?select=epoch,epoch_slot,block_time&limit=3" -H "Accept: text/csv" # epoch,epoch_slot,block_time # 318,28491,1643607582 @@ -133,6 +133,10 @@ info: Yet, there may be cases where the above restrictions may need exceptions (for example, an explorer or a wallet might need more connections than above - going beyond the Burst Limit). For such cases, it is best to approach the team and we can work towards a solution. + # Authentication + + While Koios public tier remains unauthenticated and allows queries without any authentication, it has low limits to prevent actions against an erroraneous query/loop from a consumer. There is also a Free tier which requires setting up Bearer Auth token that is linked to the owner's wallet account (which can be connected to via [Koios website](https://koios.rest/pricing/Pricing.html) ). + The examples across this API site already [supports authentication](/#auth), for you to use in the queries. # Community projects diff --git a/specs/templates/2-api-params.yaml b/specs/templates/2-api-params.yaml index 40dac816..5c1a3d7d 100644 --- a/specs/templates/2-api-params.yaml +++ b/specs/templates/2-api-params.yaml @@ -170,6 +170,16 @@ parameters: in: query required: false allowEmptyValue: true + _extended: + deprecated: false + name: _extended + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + schema: + type: boolean + example: "false" + in: query + required: false + allowEmptyValue: true _history: deprecated: false name: _history diff --git a/specs/templates/3-api-requestBodies.yaml b/specs/templates/3-api-requestBodies.yaml index 0b69563f..4ef376b5 100644 --- a/specs/templates/3-api-requestBodies.yaml +++ b/specs/templates/3-api-requestBodies.yaml @@ -35,6 +35,29 @@ requestBodies: _addresses: - ##payment_addresses1_rb## - ##payment_addresses2_rb## + payment_addresses_with_extended: + content: + application/json: + schema: + required: + - _addresses + type: object + properties: + _addresses: + format: text + type: array + items: + type: string + description: Array of Cardano payment address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _addresses: + - ##payment_addresses1_rb## + - ##payment_addresses2_rb## + _extended: true address_txs: content: application/json: @@ -107,6 +130,32 @@ requestBodies: _stake_addresses: - ##stake_addresses1_rb## - ##stake_addresses2_rb## + _first_only: false + _empty: false + + stake_addresses_with_extended: + content: + application/json: + schema: + required: + - _stake_addresses + type: object + properties: + _stake_addresses: + format: text + type: array + items: + type: string + description: Array of Cardano stake address(es) in bech32 format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _stake_addresses: + - ##stake_addresses1_rb## + - ##stake_addresses2_rb## + _extended: true stake_addresses: content: application/json: @@ -162,10 +211,15 @@ requestBodies: items: type: string description: Array of Cardano payment credential(s) in hex format + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call example: _payment_credentials: - ##credential_txs_payment_credentials1_rb## - ##credential_txs_payment_credentials2_rb## + _extended: true tx_ids: content: application/json: @@ -227,6 +281,22 @@ requestBodies: - ##pool_ids_pool_bech32_ids1_rb## - ##pool_ids_pool_bech32_ids2_rb## - ##pool_ids_pool_bech32_ids3_rb## + script_hashes: + content: + application/json: + schema: + type: object + properties: + _script_hashes: + format: text + type: array + items: + type: string + description: Array of Cardano script hashes + example: + _script_hashes: + - ##script_hashes1_rb## + - ##script_hashes2_rb## datum_hashes: content: application/json: @@ -263,3 +333,51 @@ requestBodies: _asset_list: - ##asset1_rb## - ##asset2_rb## + asset_list_with_extended: + content: + application/json: + schema: + required: + - _asset_list + type: object + properties: + _asset_list: + format: text + type: array + description: Array of array of policy ID and asset names (hex) + items: + type: array + items: + type: string + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _asset_list: + - ##asset1_rb## + - ##asset2_rb## + _extended: true + utxo_refs_with_extended: + content: + application/json: + schema: + required: + - _utxo_refs + type: object + properties: + _utxo_refs: + format: text + type: array + items: + type: string + description: Array of Cardano utxo references in the form "hash#index" + _extended: + format: boolean + type: boolean + description: Controls whether or not certain optional fields supported by a given endpoint are populated as a part of the call + example: + _utxo_refs: + - ##utxo_ref1_rb## + - ##utxo_ref2_rb## + _extended: false diff --git a/specs/templates/4-api-schemas.yaml b/specs/templates/4-api-schemas.yaml index 09703f3a..942a0582 100644 --- a/specs/templates/4-api-schemas.yaml +++ b/specs/templates/4-api-schemas.yaml @@ -111,20 +111,59 @@ schemas: type: string description: JSON encoded data with details about the parameter update example: {"decentralisation": 0.9} + reserve_withdrawals: + type: array + items: + properties: + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + amount: + $ref: "#/components/schemas/pool_delegators/items/properties/amount" + stake_address: + $ref: "#/components/schemas/account_history/items/properties/stake_address" pool_list: type: array items: properties: pool_id_bech32: - type: string - nullable: true - description: Bech32 representation of pool ID - example: pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + pool_id_hex: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_hex" + active_epoch_no: + $ref: "#/components/schemas/pool_info/items/properties/active_epoch_no" + margin: + $ref: "#/components/schemas/pool_info/items/properties/margin" + fixed_cost: + $ref: "#/components/schemas/pool_info/items/properties/fixed_cost" + pledge: + $ref: "#/components/schemas/pool_info/items/properties/pledge" + reward_addr: + $ref: "#/components/schemas/pool_info/items/properties/reward_addr" + owners: + $ref: "#/components/schemas/pool_info/items/properties/margin" + relays: + $ref: "#/components/schemas/pool_info/items/properties/margin" ticker: type: string nullable: true description: Pool ticker - example: JAZZ + example: AHL + meta_url: + $ref: "#/components/schemas/pool_info/items/properties/margin" + meta_hash: + $ref: "#/components/schemas/pool_info/items/properties/margin" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/margin" + retiring_epoch: + $ref: "#/components/schemas/pool_info/items/properties/margin" pool_history_info: type: array items: @@ -196,26 +235,32 @@ schemas: $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" vrf_key_hash: type: string + nullable: true description: Pool VRF key hash example: 25efdad1bc12944d38e4e3c26c43565bec84973a812737b163b289e87d0d5ed3 margin: type: number + nullable: true description: Margin (decimal format) example: 0.1 fixed_cost: type: string + nullable: true description: Pool fixed cost per epoch example: "500000000" pledge: type: string + nullable: true description: Pool pledge in lovelace example: "64000000000000" reward_addr: type: string + nullable: true description: Pool reward address example: stake1uy6yzwsxxc28lfms0qmpxvyz9a7y770rtcqx9y96m42cttqwvp4m5 owners: type: array + nullable: true items: type: string description: Pool (co)owner address @@ -378,6 +423,26 @@ schemas: type: string description: Latest transaction hash used for delegation by the account example: 368d08fe86804d637649341d3aec4a9baa7dffa6d00f16de2ba9dba814f1c948 + pool_registrations: + type: array + nullable: true + items: + type: object + properties: + pool_id_bech32: + $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + block_hash: + $ref: "#/components/schemas/blocks/items/properties/hash" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + epoch_no: + $ref: "#/components/schemas/epoch_info/items/properties/epoch_no" + epoch_slot: + $ref: "#/components/schemas/blocks/items/properties/epoch_slot" + active_epoch_no: + $ref: "#/components/schemas/pool_updates/items/properties/active_epoch_no" pool_delegators_history: type: array nullable: true @@ -426,6 +491,7 @@ schemas: active_epoch_no: type: integer description: Epoch number in which the update becomes active + nullable: true example: 324 vrf_key_hash: $ref: "#/components/schemas/pool_info/items/properties/vrf_key_hash" @@ -447,8 +513,11 @@ schemas: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" - pool_status: - $ref: "#/components/schemas/pool_info/items/properties/pool_status" + update_type: + type: string + description: Type of update task + enum: ["registration", "deregistration"] + example: registered retiring_epoch: $ref: "#/components/schemas/pool_info/items/properties/retiring_epoch" pool_relays: @@ -460,6 +529,8 @@ schemas: $ref: "#/components/schemas/pool_info/items/properties/pool_id_bech32" relays: $ref: "#/components/schemas/pool_info/items/properties/relays" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" pool_metadata: type: array items: @@ -473,6 +544,8 @@ schemas: $ref: "#/components/schemas/pool_info/items/properties/meta_hash" meta_json: $ref: "#/components/schemas/pool_info/items/properties/meta_json" + pool_status: + $ref: "#/components/schemas/pool_info/items/properties/pool_status" epoch_info: type: array items: @@ -641,8 +714,8 @@ schemas: description: The hash of the first block where these parameters are valid example: f9dc2a2fc3a2db09a71af007a740261de585afc9e3022b8e30535592ff4dd9e5 cost_models: - type: string - description: The per language cost models + type: object + description: The per language cost model in JSON example: null nullable: true price_mem: @@ -832,17 +905,21 @@ schemas: properties: block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" - tx_hashes: - type: array - items: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + tx_hash: + $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" + block_height: + $ref: "#/components/schemas/blocks/items/properties/block_height" + block_time: + $ref: "#/components/schemas/blocks/items/properties/block_time" address_info: type: array items: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" balance: type: string description: Sum of all UTxO values beloning to address @@ -861,9 +938,9 @@ schemas: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: @@ -877,7 +954,7 @@ schemas: reference_script: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" address_txs: type: array items: @@ -897,22 +974,17 @@ schemas: type: object properties: address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" - asset_list: - $ref: "#/components/schemas/account_assets/items/properties/asset_list" - credential_txs: - $ref: "#/components/schemas/address_txs" - credential_utxos: - type: array - items: - type: object - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" - tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" - value: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + $ref: "#/components/schemas/utxo_infos/items/properties/address" + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_list: type: array items: @@ -933,7 +1005,9 @@ schemas: enum: ["registered", "not registered"] example: registered delegated_pool: - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" + nullable: true + allOf: + - $ref: "#/components/schemas/pool_list/items/properties/pool_id_bech32" total_balance: type: string description: Total balance of the account including UTxO, rewards and MIRs (in lovelace) @@ -962,23 +1036,67 @@ schemas: type: string description: Total treasury MIR value of the account example: "0" - account_utxos: + utxo_infos: type: array items: type: object properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_hash" + type: string + description: Hash identifier of the transaction + example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e tx_index: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/tx_index" + type: integer + description: Index of UTxO in the transaction + example: 0 address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + type: string + description: A Cardano payment/base address (bech32 encoded) + example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp value: $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/value" + stake_address: + $ref: "#/components/schemas/address_info/items/properties/stake_address" + payment_cred: + type: string + description: Payment credential + example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 + nullable: true + epoch_no: + $ref: "#/components/schemas/blocks/items/properties/epoch_no" block_height: $ref: "#/components/schemas/blocks/items/properties/block_height" block_time: $ref: "#/components/schemas/blocks/items/properties/block_time" + datum_hash: + $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + inline_datum: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum" + reference_script: + $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/reference_script" + asset_list: + type: array + nullable: true + description: An array of assets on the UTxO + items: + properties: + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + type: string + description: Quantity of assets on the UTxO + example: 1 + is_spent: + type: boolean + description: True if the UTXO has been spent + example: true + account_rewards: type: array items: @@ -1023,7 +1141,7 @@ schemas: enum: ["registration", "delegation", "withdrawal", "deregistration"] example: "registration" tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" epoch_no: $ref: "#/components/schemas/blocks/items/properties/epoch_no" epoch_slot: @@ -1042,7 +1160,7 @@ schemas: addresses: type: array items: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" account_assets: type: array items: @@ -1050,25 +1168,16 @@ schemas: properties: stake_address: $ref: "#/components/schemas/account_history/items/properties/stake_address" - asset_list: - type: array - items: - type: object - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - type: integer - description: Asset decimals - example: 6 - quantity: - type: string - description: Asset quantity owned by account - example: 990000 + policy_id: + $ref: "#/components/schemas/asset_info/items/properties/policy_id" + asset_name: + $ref: "#/components/schemas/asset_info/items/properties/asset_name" + fingerprint: + $ref: "#/components/schemas/asset_info/items/properties/fingerprint" + decimals: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + quantity: + $ref: "#/components/schemas/asset_addresses/items/properties/quantity" account_history: type: array items: @@ -1100,9 +1209,7 @@ schemas: type: object properties: tx_hash: - type: string - description: Hash identifier of the transaction - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" block_hash: $ref: "#/components/schemas/blocks/items/properties/hash" block_height: @@ -1169,25 +1276,17 @@ schemas: type: object properties: bech32: - type: string - description: A Cardano payment/base address (bech32 encoded) where funds were sent or change to be returned - example: addr1q80rc8zj06yzdwwdyqc03rm4l3zv6n89rxuaak0t099n09yssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqad9mkw + $ref: "#/components/schemas/utxo_infos/items/properties/address" cred: type: string description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -1237,23 +1336,7 @@ schemas: description: Value (json) example: null asset_list: - type: array - nullable: true - description: An array of assets on the UTxO - items: - properties: - policy_id: - $ref: "#/components/schemas/asset_info/items/properties/policy_id" - asset_name: - $ref: "#/components/schemas/asset_info/items/properties/asset_name" - fingerprint: - $ref: "#/components/schemas/asset_info/items/properties/fingerprint" - decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - quantity: - type: string - description: Quantity of assets on the UTxO - example: 1 + $ref: "#/components/schemas/utxo_infos/items/properties/asset_list" withdrawals: type: array description: Array of withdrawals with-in a transaction @@ -1282,7 +1365,7 @@ schemas: fingerprint: $ref: "#/components/schemas/asset_info/items/properties/fingerprint" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" quantity: type: string description: Quantity of minted assets (negative on burn) @@ -1320,7 +1403,7 @@ schemas: items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" script_json: type: object description: JSON representation of the timelock script (null for other script types) @@ -1345,6 +1428,7 @@ schemas: } plutus_contracts: type: array + nullable: true description: Plutus contracts present in transaction (if any) items: properties: @@ -1354,15 +1438,11 @@ schemas: example: addr1w999n67e86jn6xal07pzxtrmqynspgx0fwmcmpua4wc6yzsxpljz3 nullable: true script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + $ref: "#/components/schemas/script_info/items/properties/script_hash" bytecode: - type: string - description: CBOR-encoded Plutus script data - example: 5911ea010000332333222332233223322332232323332223233322232333333332222222232333222323333222232323322323332223233322232323322332232323333322222332233223322332233223322223223223232533530333330083333573466e1cd55cea8032400046460a000264646464646666ae68cdc39aab9d5004480008cccc8888cccc16c01000c008004dd71aba15004375c6ae85400cdd71aba15002375a6ae84d5d1280111a8279a982819ab9c491035054310005149926135744a00226ae8940044d55cf280089baa001357426aae79401c8d4124d4c128cd5ce2481035054310004b499263333573466e1d40112002205323333573466e1d40152000205523504a35304b335738921035054310004c49926498cccd5cd19b8735573aa004900011980599191919191919191919191999ab9a3370e6aae754029200023333333333019335027232323333573466e1cd55cea8012400046603e60746ae854008c0b0d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80519a8138141aba150093335502e75ca05a6ae854020ccd540b9d728169aba1500733502704335742a00c66a04e66aa0a8098eb4d5d0a8029919191999ab9a3370e6aae754009200023350213232323333573466e1cd55cea80124000466a05266a084eb4d5d0a80118239aba135744a00446a0ba6a60bc66ae712401035054310005f49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502733504275a6ae854008c11cd5d09aba2500223505d35305e3357389201035054310005f49926135573ca00226ea8004d5d09aba2500223505935305a3357389201035054310005b49926135573ca00226ea8004d5d0a80219a813bae35742a00666a04e66aa0a8eb88004d5d0a801181c9aba135744a00446a0aa6a60ac66ae71241035054310005749926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f181d9aba135573ca00646666ae68cdc3a801240084603a608a6ae84d55cf280211999ab9a3370ea00690011180e98181aba135573ca00a46666ae68cdc3a80224000460406eb8d5d09aab9e50062350503530513357389201035054310005249926499264984d55cea80089baa001357426ae8940088d4124d4c128cd5ce249035054310004b49926104a1350483530493357389201035054350004a4984d55cf280089baa001135573a6ea80044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503b2233335573e0024a01c466a01a60086ae84008c00cd5d100101991919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a60626ae854008cd404c0b4d5d09aba25002235036353037335738921035054310003849926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba25002235032353033335738921035054310003449926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540d488c8cccd55cf80112804919a80419aa81898031aab9d5002300535573ca00460086ae8800c0b84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae712401035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa016600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004448848cc00400c008448004848c0040088004888848cccc00401401000c00880044880084880048004448c8c00400488cc00cc008008004c8ccc888c8c8cc88cc88ccc888c8c8c8c8cc88c8c8cc88c8cccc8888c8c8c8c8c8c8c8c8ccc888c8ccc888ccc888cccccccc88888888cc88ccccc88888cccc8888cc88cc88cc88ccc888cc88cc88cc88cc88cc88c8c8c8cc88c8c8c8c8c8c8cc88c8c8c8c8c8c8cc888c888c8c94cd4c1240104cc0352401297369676e617475726520646f6573206e6f74206d617463682063726561746f7220696e20646174756d0033223530200022222222222533535032333553068120015027253353079333573466e3c0300041ec1e84d40d4004540d000c841ec41e54008c04540144cc054cc03524012f65787065637465642063726561746f7220746f2067657420616c6c206f66207768617420736865206f72646572656400330153350133335500e305c12001504f350481223335501b2253353070333505006353353502a3235302900122335304c0022350300012502f300e00221001132635300b335738920117696e76616c6964207075626c6963206b657920686173680000c498c05540244cc010c0340080044004004c8d4c08400488888888880254010ccd41354138c1894014c04920c08db7013350133335500e305c12001504f350481223335501b22533530703005002133004335506500d30100020011001001300d5004333504d504e30625005333504d504e533535026301a00321300a300d0011630124830236dc04cc0352401276f6e6c79206d617463686573206f66207061697273206f66206f726465727320616c6c6f77656400533535063350481223335501b225335307030050021330040020011001001300d50041306a162215335350650011306c16221533535067001107222130701623253353502732353024001222001500121333504e223530280022235302a0032232335304e0052335304f004253353077333573466e3c0080041e41e05400c41e081e08cd4c13c01081e094cd4c1dcccd5cd19b8f002001079078150031078153353502e0032153353502f00221335304c0022335304d002233530510022335305200223306d002001207b23353052002207b23306d00200122207b222335304f004207b2225335307c333573466e1c01800c1f81f454cd4c1f0ccd5cd19b8700500207e07d1333573466e1c0100041f81f441f441f441d854cd4d40b8004841d841d8c03140094cd4d40a0c07001484cd54190034c03c0045841b84c0300044d4c068004880084d4c02800480044800480048d4c0680048880088d4c06400488800c8d4c05000488888888880288d4c05400488004894cd4c184004418c4cd5ce001031089119aa8011a82600090009091800801100091a982c0009111002119a82999aa82b245003350533355056489000015054505412233355304b120013500550032353550560012233355304e120013500850062353550590012233353550490012330614800000488cc1880080048cc184005200000133040002001133500400105a22533530590021001105a123350492233353500400322002002001353500200122001122123300100300212001112232001320013550582253353504e00110032213300600230040012353003001223530070022222222222533335302600b21501b21501b21501b2133355305012001500f2353015001225335306353353063333573466e3cd4c0bc00888008d4c0bc010880081941904ccd5cd19b8735302f0022200135302f00422001065064106413501f0031501e00b13350432253353500d002210031001500c2212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220012212330010030022001121223002003112200112001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200112335001501d501e1220021220012001120011200113002012133500b2233300301300200150162223355300e1200123535501a00122335501d002335530111200123535501d001223355020002333535500d00123300a4800000488cc02c0080048cc028005200000133004002001223355300c1200123535501800122335501b00233353550080012335530101200123535501c00122335501f00235500f001001223335550080150020012335530101200123535501c00122335501f00235500d0010013335550030100020011112223335530041200150153355300c1200123535501800122335501b00235500b0013335530041200122353550190022253353021333553011120013500d33500f22533530230021025100102223535501c001223300a0020050061003133501900400350160013355300c120012353550180012232335501c00330010053200135502322533535019001135500b0032213535501e00222533530263300c00200813355010007001130060030023200135501c221122253353501500110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120011233500722333535004003220020020013535002001220011221233001003002120013200135501422112253353500c0011500e22133500f3004002335530061200100400132001355013221122253353500c00113535006003220012213335350080052200230040023335530071200100500400112212330010030021200122333573466e3c00800404404088cdc000100088911801000919991119a80319aa80480199a80319aa804801000a803a8039a980380091110019a980380091110011a98038009111000889100109109119800802001890008891091980080180108900091110919998008028020018011000900211199ab9a3371000400200800a244004244002400222464600200244660066004004003 + $ref: "#/components/schemas/script_info/items/properties/bytes" size: - type: integer - description: The size of the CBOR serialised script (in bytes) - example: 234895 + $ref: "#/components/schemas/script_info/items/properties/size" valid_contract: type: boolean description: True if the contract is valid or there is no contract @@ -1422,17 +1502,11 @@ schemas: description: Payment credential example: de3c1c527e8826b9cd2030f88f75fc44cd4ce519b9ded9eb794b3794 stake_addr: - nullable: true - allOf: - - $ref: "#/components/schemas/account_history/items/properties/stake_address" + $ref: "#/components/schemas/address_info/items/properties/stake_address" tx_hash: - type: string - description: Hash of transaction for UTxO - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: Index of UTxO in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" value: type: string description: Total sum of ADA on the UTxO @@ -1447,7 +1521,7 @@ schemas: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" metadata: type: object nullable: true @@ -1468,7 +1542,7 @@ schemas: items: properties: tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" num_confirmations: type: integer description: Number of block confirmations @@ -1522,9 +1596,7 @@ schemas: items: properties: payment_address: - type: string - description: A Cardano payment/base address (bech32 encoded) for transaction's input UTxO - example: addr1qxkfe8s6m8qt5436lec3f0320hrmpppwqgs2gah4360krvyssntpwjcz303mx3h4avg7p29l3zd8u3jyglmewds9ezrqdc3cxp + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: type: string description: Asset balance on the payment address @@ -1535,7 +1607,7 @@ schemas: items: properties: payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" asset_summary: type: array items: @@ -1627,6 +1699,11 @@ schemas: decimals: type: integer example: 0 + cip68_metadata: + type: object + description: CIP 68 metadata if present for asset + nullable: true + example: {"222": {"fields": [{"map": [{"k": {"bytes": "6e616d65"}, "v": {"bytes": "74657374"}}]}], "constructor": 0}} asset_history: type: array items: @@ -1667,7 +1744,7 @@ schemas: asset_name: $ref: "#/components/schemas/asset_info/items/properties/asset_name" payment_address: - $ref: "#/components/schemas/asset_addresses/items/properties/payment_address" + $ref: "#/components/schemas/utxo_infos/items/properties/address" quantity: $ref: "#/components/schemas/asset_addresses/items/properties/quantity" policy_asset_info: @@ -1707,67 +1784,65 @@ schemas: total_supply: $ref: "#/components/schemas/asset_info/items/properties/total_supply" decimals: - $ref: "#/components/schemas/account_assets/items/properties/asset_list/items/properties/decimals" - asset_txs: - type: array - description: An array of Tx hashes that included the given asset (latest first) - items: - properties: - tx_hash: - $ref: "#/components/schemas/tx_info/items/properties/tx_hash" - epoch_no: - $ref: "#/components/schemas/blocks/items/properties/epoch_no" - block_height: - $ref: "#/components/schemas/blocks/items/properties/block_height" - block_time: - $ref: "#/components/schemas/blocks/items/properties/block_time" - native_script_list: + $ref: "#/components/schemas/asset_info/items/properties/token_registry_metadata/properties/decimals" + script_info: type: array items: properties: script_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/script_hash" + type: string + description: Hash of a script + example: bfa7ffa9b2e164873db6ac6d0528c82e212963bc62e10fd1d81da4af creation_tx_hash: - $ref: "#/components/schemas/plutus_script_list/items/properties/creation_tx_hash" + type: string + description: Hash of the script creation transaction + example: 255f061502ad83230351fbcf2d9fade1b5d118d332f92c9861075010a1fe3fbe type: type: string description: Type of the script - enum: ["timelock", "multisig"] - example: timelock - plutus_script_list: + enum: ["plutusV1","plutusV2","timelock","multisig"] + example: plutusV1 + value: + type: object + nullable: true + description: Data in JSON format + example: null + bytes: + type: string + description: Script bytes (cborSeq) + example: 5907f4010000332323232323232323233223232323232332232323232322223232533532533533355300712001323212330012233350052200200200100235001220011233001225335002101710010142325335333573466e3cd400488008d4020880080580544ccd5cd19b873500122001350082200101601510153500122002353500122002222222222200a101413357389201115554784f206e6f7420636f6e73756d6564000133333573466e1cd55cea8012400046644246600200600464646464646464646464646666ae68cdc39aab9d500a480008cccccccccc888888888848cccccccccc00402c02802402001c01801401000c008cd40508c8c8cccd5cd19b8735573aa0049000119910919800801801180f9aba150023019357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854028cd4050054d5d0a804999aa80bbae501635742a010666aa02eeb94058d5d0a80399a80a0109aba15006335014335502402275a6ae854014c8c8c8cccd5cd19b8735573aa00490001199109198008018011919191999ab9a3370e6aae754009200023322123300100300233502575a6ae854008c098d5d09aba2500223263533573805e05c05a05826aae7940044dd50009aba150023232323333573466e1cd55cea8012400046644246600200600466a04aeb4d5d0a80118131aba135744a004464c6a66ae700bc0b80b40b04d55cf280089baa001357426ae8940088c98d4cd5ce01581501481409aab9e5001137540026ae854010cd4051d71aba15003335014335502475c40026ae854008c070d5d09aba2500223263533573804e04c04a04826ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d400520062321222230040053019357426aae79400c8cccd5cd19b875002480108c848888c008014c06cd5d09aab9e500423333573466e1d400d20022321222230010053015357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6a66ae7008808408007c0780740704d55cea80089baa001357426ae8940088c98d4cd5ce00d80d00c80c080c89931a99ab9c4910350543500019018135573ca00226ea8004c8004d5405888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000448c88c008dd6000990009aa80b111999aab9f00125009233500830043574200460066ae880080548c8c8c8cccd5cd19b8735573aa00690001199911091998008020018011919191999ab9a3370e6aae7540092000233221233001003002301735742a00466a01c02c6ae84d5d1280111931a99ab9c01b01a019018135573ca00226ea8004d5d0a801999aa803bae500635742a00466a014eb8d5d09aba2500223263533573802e02c02a02826ae8940044d55cf280089baa0011335500175ceb44488c88c008dd5800990009aa80a11191999aab9f0022500823350073355017300635573aa004600a6aae794008c010d5d100180a09aba100111220021221223300100400312232323333573466e1d4005200023212230020033005357426aae79400c8cccd5cd19b8750024800884880048c98d4cd5ce00980900880800789aab9d500113754002464646666ae68cdc39aab9d5002480008cc8848cc00400c008c014d5d0a8011bad357426ae8940088c98d4cd5ce00800780700689aab9e5001137540024646666ae68cdc39aab9d5001480008dd71aba135573ca004464c6a66ae7003803403002c4dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6a66ae7004404003c0380340304d55cea80089baa0012323333573466e1d40052002200523333573466e1d40092000200523263533573801a01801601401226aae74dd5000891001091000919191919191999ab9a3370ea002900610911111100191999ab9a3370ea004900510911111100211999ab9a3370ea00690041199109111111198008048041bae35742a00a6eb4d5d09aba2500523333573466e1d40112006233221222222233002009008375c6ae85401cdd71aba135744a00e46666ae68cdc3a802a400846644244444446600c01201060186ae854024dd71aba135744a01246666ae68cdc3a8032400446424444444600e010601a6ae84d55cf280591999ab9a3370ea00e900011909111111180280418071aba135573ca018464c6a66ae7004c04804404003c03803403002c0284d55cea80209aab9e5003135573ca00426aae7940044dd50009191919191999ab9a3370ea002900111999110911998008028020019bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c8488c00800cc020d5d09aab9e500623263533573801801601401201026aae75400c4d5d1280089aab9e500113754002464646666ae68cdc3a800a400446424460020066eb8d5d09aab9e500323333573466e1d400920002321223002003375c6ae84d55cf280211931a99ab9c009008007006005135573aa00226ea800444888c8c8cccd5cd19b8735573aa0049000119aa80518031aba150023005357426ae8940088c98d4cd5ce00480400380309aab9e5001137540029309000a490350543100112212330010030021123230010012233003300200200133512233002489209366f09fe40eaaeb17d3cb6b0b61e087d664174c39a48a986f86b2b0ba6e2a7b00480008848cc00400c0088005 + size: + type: integer + description: The size of the CBOR serialised script (in bytes) + example: 2039 + script_list: type: array items: properties: script_hash: - type: string - description: Hash of a script - example: d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8 + $ref: "#/components/schemas/script_info/items/properties/script_hash" creation_tx_hash: - type: string - description: Hash of the script creation transaction - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" + type: + $ref: "#/components/schemas/script_info/items/properties/type" + size: + $ref: "#/components/schemas/script_info/items/properties/size" script_redeemers: type: array items: type: object properties: script_hash: - type: string - description: Hash of Transaction for which details are being shown - example: f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e + $ref: "#/components/schemas/script_info/items/properties/script_hash" redeemers: type: array items: type: object properties: tx_hash: - type: string - description: Hash of Transaction containing the redeemer - example: fda6c7697009237975ffc30f36666addf4c6e2a2c6f90411a24431b700baaab1 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_hash" tx_index: - type: integer - description: The index of the redeemer pointer in the transaction - example: 0 + $ref: "#/components/schemas/utxo_infos/items/properties/tx_index" unit_mem: description: The budget in Memory to run a script example: 520448 @@ -1799,17 +1874,17 @@ schemas: nullable: true example: 5a595ce795815e81d22a1a522cf3987d546dc5bb016de61b002edd63a5413ec4 datum_value: - type: object - description: The actual data in json format - example: { "bytes": "3c33" } + $ref: "#/components/schemas/script_info/items/properties/value" datum_info: type: array items: type: object properties: - hash: + datum_hash: $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_hash" + creation_tx_hash: + $ref: "#/components/schemas/script_info/items/properties/creation_tx_hash" value: - $ref: "#/components/schemas/script_redeemers/items/properties/redeemers/items/properties/datum_value" + $ref: "#/components/schemas/script_info/items/properties/value" bytes: - $ref: "#/components/schemas/tx_info/items/properties/outputs/items/properties/inline_datum/properties/bytes" + $ref: "#/components/schemas/script_info/items/properties/bytes" diff --git a/specs/templates/api-main.yaml b/specs/templates/api-main.yaml index c411c04d..86168694 100644 --- a/specs/templates/api-main.yaml +++ b/specs/templates/api-main.yaml @@ -1,11 +1,12 @@ openapi: 3.0.2 #!info!# servers: - - url: https://api.koios.rest/api/v0 - - url: https://guild.koios.rest/api/v0 - - url: https://preview.koios.rest/api/v0 - - url: https://preprod.koios.rest/api/v0 + - url: https://api.koios.rest/api/v1 + - url: https://guild.koios.rest/api/v1 + - url: https://preview.koios.rest/api/v1 + - url: https://preprod.koios.rest/api/v1 paths: + /tip: #RPC get: tags: @@ -86,6 +87,45 @@ paths: $ref: "#/components/responses/NotFound" summary: Param Update Proposals description: Get all parameter update proposals submitted to the chain starting Shelley era + /reserve_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from reserves against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Reserve Withdrawals + description: List of all withdrawals from reserves against stake accounts + /treasury_withdrawals: #RPC + get: + tags: + - Network + responses: + "200": + description: Array of withdrawals from treasury against stake accounts + content: + application/json: + schema: + $ref: "#/components/schemas/reserve_withdrawals" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Treasury Withdrawals + description: List of all withdrawals from treasury against stake accounts + /epoch_info: #RPC get: tags: @@ -153,6 +193,7 @@ paths: summary: Epoch's Block Protocols description: >- Get the information about block protocol distribution in epoch + /blocks: get: tags: @@ -214,28 +255,29 @@ paths: $ref: "#/components/responses/NotFound" summary: Block Transactions description: Get a list of all transactions included in provided blocks - /tx_info: #RPC + + /utxo_info: #RPC post: tags: - Transactions requestBody: - $ref: "#/components/requestBodies/tx_ids" + $ref: "#/components/requestBodies/utxo_refs_with_extended" responses: "200": - description: Array of detailed information about transaction(s) + description: Array of UTXO details content: application/json: schema: - $ref: "#/components/schemas/tx_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Information - description: Get detailed information about transaction(s) - /tx_utxos: #RPC + summary: UTxO Info + description: Get UTxO set for requested UTxO references + /tx_info: #RPC post: tags: - Transactions @@ -243,19 +285,19 @@ paths: $ref: "#/components/requestBodies/tx_ids" responses: "200": - description: Array of inputs and outputs for given transaction(s) + description: Array of detailed information about transaction(s) content: application/json: schema: - $ref: "#/components/schemas/tx_utxos" + $ref: "#/components/schemas/tx_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction UTxOs [DEPRECATED] - description: Get UTxO set (inputs/outputs) of transactions. + summary: Transaction Information + description: Get detailed information about transaction(s) /tx_metadata: #RPC post: tags: @@ -309,7 +351,7 @@ paths: # If using a CLI-generated tx file, please ensure to deserialise (using `xxd -p -r <<< $(jq .cborHex ${tx.signed}) > ${data}`) first before submitting. curl -X POST \ --header "Content-Type: application/cbor" \ - --data-binary ${data} https://api.koios.rest/api/v0/submittx + --data-binary @${data} https://api.koios.rest/api/v1/submittx responses: "202": description: OK @@ -345,8 +387,31 @@ paths: $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Transaction Status (Block Confirmations) + summary: Transaction Status description: Get the number of block confirmations for a given transaction hash list + /tx_utxos: #RPC + post: + tags: + - Transactions + deprecated: true + requestBody: + $ref: "#/components/requestBodies/tx_ids" + responses: + "200": + description: Array of inputs and outputs for given transaction(s) + content: + application/json: + schema: + $ref: "#/components/schemas/tx_utxos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Transaction UTxOs + description: Get UTxO set (inputs/outputs) of transactions [DEPRECATED - Use /utxo_info instead]. + /address_info: #RPC post: tags: @@ -368,27 +433,27 @@ paths: $ref: "#/components/responses/NotFound" summary: Address Information description: Get address info - balance, associated stake address (if any) and UTxO set for given addresses - /address_txs: #RPC + /address_utxos: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/address_txs" + $ref: "#/components/requestBodies/payment_addresses_with_extended" responses: "200": - description: Array of transaction hashes + description: Array of address UTXOs content: application/json: schema: - $ref: "#/components/schemas/address_txs" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Transactions - description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) + summary: Address UTXOs + description: Get UTxO set for given addresses /credential_utxos: #RPC post: tags: @@ -401,7 +466,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_utxos" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": @@ -409,28 +474,28 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: UTxOs from payment credentials - description: Get a list of UTxO against input payment credential array including their balances - /address_assets: #RPC + description: Get UTxO details for requested payment credentials + /address_txs: #RPC post: tags: - Address requestBody: - $ref: "#/components/requestBodies/payment_addresses" + $ref: "#/components/requestBodies/address_txs" responses: "200": - description: Array of address-owned assets + description: Array of transaction hashes content: application/json: schema: - $ref: "#/components/schemas/address_assets" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Address Assets - description: Get the list of all the assets (policy, name and quantity) for given addresses + summary: Address Transactions + description: Get the transaction hash list of input address array, optionally filtering after specified block height (inclusive) /credential_txs: #RPC post: tags: @@ -443,7 +508,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/credential_txs" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": @@ -452,6 +517,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Transactions from payment credentials description: Get the transaction hash list of input payment credential array, optionally filtering after specified block height (inclusive) + /address_assets: #RPC + post: + tags: + - Address + requestBody: + $ref: "#/components/requestBodies/payment_addresses" + responses: + "200": + description: Array of address-owned assets + content: + application/json: + schema: + $ref: "#/components/schemas/address_assets" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Address Assets + description: Get the list of all the assets (policy, name and quantity) for given addresses + /account_list: get: tags: @@ -492,48 +579,70 @@ paths: $ref: "#/components/responses/NotFound" summary: Account Information description: Get the account information for given stake addresses - /account_utxos: #RPC - get: + /account_info_cached: #RPC + post: tags: - Stake Account - parameters: - - $ref: "#/components/parameters/_stake_address" + requestBody: + $ref: "#/components/requestBodies/stake_addresses" responses: "200": - description: Array of account UTxOs associated with stake address + description: Array of account information content: application/json: schema: - $ref: "#/components/schemas/account_utxos" + $ref: "#/components/schemas/account_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account UTxOs - description: Get a list of all UTxOs for a given stake address (account) - /account_info_cached: #RPC + summary: Account Information (Cached) + description: Get the cached account information for given stake addresses (effective for performance query against registered accounts) + /account_utxos: #RPC post: tags: - Stake Account requestBody: - $ref: "#/components/requestBodies/stake_addresses" + $ref: "#/components/requestBodies/stake_addresses_with_extended" responses: "200": - description: Array of account information + description: Array of account UTxOs associated with given stake addresses content: application/json: schema: - $ref: "#/components/schemas/account_info" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Account Information (Cached) - description: Get the cached account information for given stake addresses, effective for registered accounts + summary: UTxOs for stake addresses (accounts) + description: Get a list of all UTxOs for given stake addresses (account)s + /account_txs: #RPC + get: + tags: + - Stake Account + parameters: + - $ref: "#/components/parameters/_stake_address" + - $ref: "#/components/parameters/_after_block_height" + responses: + "200": + description: Array of Txs associated with stake address (account) + content: + application/json: + schema: + $ref: "#/components/schemas/address_txs" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Account Txs + description: Get a list of all Txs for a given stake address (account) /account_rewards: #RPC post: tags: @@ -642,6 +751,7 @@ paths: $ref: "#/components/responses/NotFound" summary: Account History description: Get the staking history of given stake addresses (accounts) + /asset_list: get: tags: @@ -661,95 +771,89 @@ paths: $ref: "#/components/responses/NotFound" summary: Asset List description: Get the list of all native assets (paginated) - /asset_token_registry: + /policy_asset_list: #RPC get: tags: - Asset + parameters: + - $ref: "#/components/parameters/_asset_policy" responses: "200": - description: Array of token registry information for each asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_token_registry" + $ref: "#/components/schemas/policy_asset_list" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Token Registry - description: Get a list of assets registered via token registry on github - /asset_addresses: #RPC + summary: Policy Asset List + description: Get the list of asset under the given policy (including balances) + /asset_token_registry: get: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of token registry information for each asset content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_token_registry" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Addresses - description: Get the list of all addresses holding a given asset

    - `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects - with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to - query layers to have a dedicated cache table for themselves served via Koios.` - /asset_address_list: #RPC - get: + summary: Asset Token Registry + description: Get a list of assets registered via token registry on github + /asset_info: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + requestBody: + $ref: "#/components/requestBodies/asset_list" responses: "200": - description: Array of payment addresses holding the given token (including balances) + description: Array of detailed asset information content: application/json: schema: - $ref: "#/components/schemas/asset_addresses" + $ref: "#/components/schemas/asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Address List [DEPRECATED] - description: Get the list of all addresses holding a given asset (replaced by asset_addresses) - /asset_nft_address: #RPC - get: + summary: Asset Information (Bulk) + description: Get the information of a list of assets including first minting & token registry metadata + /asset_utxos: #RPC + post: tags: - Asset - parameters: - - $ref: "#/components/parameters/_asset_policy_nft" - - $ref: "#/components/parameters/_asset_name_nft" + requestBody: + $ref: "#/components/requestBodies/asset_list_with_extended" responses: "200": - description: Payment addresses currently holding the given NFT + description: Array of UTXOs for given asset list content: application/json: schema: - $ref: "#/components/schemas/asset_nft_address" + $ref: "#/components/schemas/utxo_infos" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: NFT Address - description: Get the address where specified NFT currently reside on. - /asset_info: #RPC + summary: Asset UTXOs + description: Get the UTXO information of a list of assets including + /asset_history: #RPC get: tags: - Asset @@ -758,61 +862,66 @@ paths: - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of asset mint/burn history content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_history" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information - description: Get the information of an asset including first minting & token registry metadata - post: + summary: Asset History + description: Get the mint/burn history of an asset + /asset_addresses: #RPC + get: tags: - Asset - requestBody: - $ref: "#/components/requestBodies/asset_list" + parameters: + - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed asset information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_info" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Information (Bulk) - description: Get the information of a list of assets including first minting & token registry metadata - /asset_history: #RPC + summary: Asset Addresses + description: Get the list of all addresses holding a given asset

    + `Note - Due to cardano's UTxO design and usage from projects, asset to addresses map can be infinite. Thus, for a small subset of active projects + with millions of transactions, these might end up with timeouts (HTTP code 504) on free layer. Such large-scale projects are free to subscribe to + query layers to have a dedicated cache table for themselves served via Koios.` + /asset_nft_address: #RPC get: tags: - Asset parameters: - - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_asset_policy_nft" + - $ref: "#/components/parameters/_asset_name_nft" responses: "200": - description: Array of asset mint/burn history + description: Payment addresses currently holding the given NFT content: application/json: schema: - $ref: "#/components/schemas/asset_history" + $ref: "#/components/schemas/asset_nft_address" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset History - description: Get the mint/burn history of an asset + summary: NFT Address + description: Get the address where specified NFT currently reside on. /policy_asset_addresses: #RPC get: tags: @@ -858,94 +967,98 @@ paths: $ref: "#/components/responses/NotFound" summary: Policy Asset Information description: Get the information for all assets under the same policy - /asset_policy_info: #RPC + /asset_summary: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of detailed information of assets under the same policy + description: Array of asset summary information content: application/json: schema: - $ref: "#/components/schemas/policy_asset_info" + $ref: "#/components/schemas/asset_summary" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Policy Information [DEPRECATED] - description: Get the information for all assets under the same policy (replaced by asset_addresses) - /policy_asset_list: #RPC + summary: Asset Summary + description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) + /asset_txs: #RPC get: tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" + - $ref: "#/components/parameters/_asset_name" + - $ref: "#/components/parameters/_after_block_height" + - $ref: "#/components/parameters/_history" responses: "200": - description: Array of detailed information of assets under the same policy + description: An array of Tx hashes that included the given asset (latest first) content: application/json: schema: - $ref: "#/components/schemas/policy_asset_list" + $ref: "#/components/schemas/address_txs" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Policy Asset List - description: Get the list of asset under the given policy (including balances) - /asset_summary: #RPC + summary: Asset Transactions + description: Get the list of current or all asset transaction hashes (newest first) + /asset_address_list: #RPC get: tags: - Asset + deprecated: true parameters: - $ref: "#/components/parameters/_asset_policy" - $ref: "#/components/parameters/_asset_name" responses: "200": - description: Array of asset summary information + description: Array of payment addresses holding the given token (including balances) content: application/json: schema: - $ref: "#/components/schemas/asset_summary" + $ref: "#/components/schemas/asset_addresses" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Summary - description: Get the summary of an asset (total transactions exclude minting/total wallets include only wallets with asset balance) - /asset_txs: #RPC + summary: Asset Address List + description: Get the list of all addresses holding a given asset [DEPRECATED - replaced by asset_addresses] + /asset_policy_info: #RPC get: + deprecated: true tags: - Asset parameters: - $ref: "#/components/parameters/_asset_policy" - - $ref: "#/components/parameters/_asset_name" - - $ref: "#/components/parameters/_after_block_height" - - $ref: "#/components/parameters/_history" responses: "200": - description: Array of Tx hashes that included the given asset + description: Array of detailed information of assets under the same policy content: application/json: schema: - $ref: "#/components/schemas/asset_txs" + $ref: "#/components/schemas/policy_asset_info" "400": $ref: "#/components/responses/BadRequest" "401": $ref: "#/components/responses/Unauthorized" "404": $ref: "#/components/responses/NotFound" - summary: Asset Transactions - description: Get the list of current or all asset transaction hashes (newest first) + summary: Asset Policy Information + description: Get the information for all assets under the same policy (DEPRECATED - replaced by policy_asset_info) + /pool_list: #RPC get: tags: @@ -964,7 +1077,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool List - description: A list of all currently registered/retiring (not retired) pools + description: List of brief info for all pools /pool_info: #RPC post: tags: @@ -1120,6 +1233,48 @@ paths: $ref: "#/components/responses/NotFound" summary: Pool Updates (History) description: Return all pool updates for all pools or only updates for specific pool if specified + /pool_registrations: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Registrations + description: Return all pool registrations initiated in the requested epoch + /pool_retirements: #RPC + get: + tags: + - Pool + parameters: + - $ref: "#/components/parameters/_epoch_no" + responses: + "200": + description: Array of historical pool updates + content: + application/json: + schema: + $ref: "#/components/schemas/pool_registrations" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Pool Retirements + description: Return all pool retirements initiated in the requested epoch /pool_relays: #RPC get: tags: @@ -1138,7 +1293,7 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Relays - description: A list of registered relays for all currently registered/retiring (not retired) pools + description: A list of registered relays for all pools /pool_metadata: #RPC post: tags: @@ -1159,7 +1314,29 @@ paths: "404": $ref: "#/components/responses/NotFound" summary: Pool Metadata - description: Metadata (on & off-chain) for all currently registered/retiring (not retired) pools + description: Metadata (on & off-chain) for all pools + + /script_info: #RPC + post: + tags: + - Script + requestBody: + $ref: "#/components/requestBodies/script_hashes" + responses: + "200": + description: List of datum information for given datum hashes + content: + application/json: + schema: + $ref: "#/components/schemas/script_info" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Datum Information + description: List of datum information for given datum hashes /native_script_list: #RPC get: tags: @@ -1170,7 +1347,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/native_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1189,7 +1366,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/plutus_script_list" + $ref: "#/components/schemas/script_list" "400": $ref: "#/components/responses/BadRequest" "401": @@ -1219,6 +1396,28 @@ paths: $ref: "#/components/responses/NotFound" summary: Script Redeemers description: List of all redeemers for a given script hash + /script_utxos: #RPC + get: + tags: + - Script + parameters: + - $ref: "#/components/parameters/_script_hash" + - $ref: "#/components/parameters/_extended" + responses: + "200": + description: List of UTXOs for a given script hash + content: + application/json: + schema: + $ref: "#/components/schemas/utxo_infos" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "404": + $ref: "#/components/responses/NotFound" + summary: Script UTXOs + description: List of all UTXOs for a given script hash /datum_info: #RPC post: tags: @@ -1240,10 +1439,51 @@ paths: $ref: "#/components/responses/NotFound" summary: Datum Information description: List of datum information for given datum hashes + /ogmios/?EvaluateTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains EvaluateTransaction payload as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?EvaluateTransaction + responses: + "200": + description: OK + "400": + description: An error occured while submitting transaction. + summary: Evaluate Transaction + description: Evaluate execution units of scripts in a well-formed transaction. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?EvaluateTransaction) for complete spec + /ogmios/?SubmitTransaction: #ogmios-api + post: + tags: + - Ogmios + x-code-samples: + - lang: "Shell" + source: | + # Assuming ${data} contains a CBOR-serialized signed transaction (base16-encoded) as documented on Ogmios v6 site. + curl -X POST \ + --header "Content-Type: application/cbor" \ + --data-binary ${data} https://api.koios.rest/api/ogmios/?SubmitTransaction + responses: + "200": + description: OK + "400": + description: An error occured while querying transaction. + summary: Submit Transaction + description: Submit a signed and serialized transaction to the network. Please refer to Ogmios documentation [here](https://ogmios.dev/api/#operation-publish-/?SubmitTransaction) for complete spec + components: #!params!# #!requestBodies!# - securitySchemes: {} + securitySchemes: + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT #!schemas!# headers: {} responses: @@ -1252,7 +1492,7 @@ components: NotFound: description: The server does not recognise the combination of endpoint and parameters provided Unauthorized: - description: The selected server has restricted the endpoint to be only usable via authentication. The authentication supplied was not authorized to access the endpoint + description: Access token is missing or invalid PartialContent: description: The result was truncated BadRequest: @@ -1270,12 +1510,12 @@ tags: - name: Transactions description: Query blockchain transaction details x-tag-expanded: false + - name: Stake Account + description: Query details about specific stake account addresses + x-tag-expanded: false - name: Address description: Query information about specific address(es) x-tag-expanded: false - - name: Account - description: Query details about specific stake account addresses - x-tag-expanded: false - name: Asset description: Query Asset related informations x-tag-expanded: false @@ -1285,4 +1525,5 @@ tags: - name: Script description: Query information about specific scripts (Smart Contracts) x-tag-expanded: false -security: [] +security: + - bearerAuth: [] diff --git a/specs/templates/example-map.json b/specs/templates/example-map.json index 07393754..1d63245c 100644 --- a/specs/templates/example-map.json +++ b/specs/templates/example-map.json @@ -8,7 +8,7 @@ }, "_epoch_no": { "m": "320", - "g": "1950", + "g": "6219", "pv": "12", "pp": "31" }, @@ -80,14 +80,14 @@ }, "_script_hash": { "m": "d8480dc869b94b80e81ec91b0abe307279311fe0e7001a9488f61ff8", - "g": "160301a01ee86d8e46cbe3aef1e3bf69bfa28c65d5be2dde56a37af8", + "g": "1392eec7d575292ae1523da65ff1b4b021886e917c8c43de54aa7cbd", "pv": "f758cf422ca0cbed7d9d6fad1eb5a3c70537d62e661ad450dd2a3810", "pp": "590555d7b5760e98ae2bdd29b356247776251dfab0a207bfce98a485" } }, "requestBodies": { "epoch_no": { - "m": "350", + "m": "409", "g": "1500", "pv": "11", "pp": "30" @@ -99,7 +99,7 @@ "pp": "['c6e65ba7878b2f8ea0ad39287d3e2fd256dc5c4160fc19bdf4c4d87e','7447454e53']" }, "asset2": { - "m": "['1d7f33bd23d85e1a25d87d86fac4f199c3197a2f7afeb662a0f34e1e','776f726c646d6f62696c65746f6b656e']", + "m": "['f0ff48bbb7bbe9d59a40f1ce90e9e9d0ff5002ec48f232b49ca0fb9a','6b6f696f732e72657374']", "g": "['313534a537bc476c86ff7c57ec511bd7f24a9d15654091b24e9c606e','41484c636f696e']", "pv": "['189e2c53985411addb8df0f3e09f70e343da69f06746c408aba672a8','15fc257714a51769e192761d674db2ee2e80137428e522f9b914debb5f785301']", "pp": "['777e6b4903dab74963ae581d39875c5dac16c09bb1f511c0af1ddda8','6141414441']" @@ -182,6 +182,18 @@ "pv": "f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c", "pp": "d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530" }, + "utxo_ref1": { + "m": "f144a8264acf4bdfe2e1241170969c930d64ab6b0996a4a45237b623f1dd670e#0", + "g": "f82e568d42604fd71424d193c86ec00c97aead2b8f018e81c3139d9e3770c735#0", + "pv": "206f6da5b0b0de45605a95f5ce7e172be9674550f7dde3838c45cbf24bab8b00#0", + "pp": "d10133964da9e443b303917fd0b7644ae3d01c133deff85b4f59416c2d00f530#0" + }, + "utxo_ref2": { + "m": "0b8ba3bed976fa4913f19adc9f6dd9063138db5b4dd29cecde369456b5155e94#0", + "g": "88ae22495123c7ee37a0bbe865243757185a302ed5359d1eae9347030628290a#0", + "pv": "f1592b29b79ae85d753913dd25644c60925a4a0683979faa33832fead4b4bd9c#0", + "pp": "145688d3619e7524510ea64c0ec6363b77a9b8da179ef9bb0273a0940d57d576#0" + }, "pool_ids_pool_bech32_ids1": { "m": "pool100wj94uzf54vup2hdzk0afng4dhjaqggt7j434mtgm8v2gfvfgp", "g": "pool19st4a2vvu78tjtyywjte2eml3kx6ynersgd2nyw0y4jvyhlfu0u", @@ -200,6 +212,18 @@ "pv": "pool1p835jxsj8py5n34lrgk6fvpgpxxvh585qm8dzvp7ups37vdet5a", "pp": "pool1ws42l6rawqjv58crs5l32v0eem3qnngpnjfd7epwd4lmjccc5cg" }, + "script_hashes1": { + "m": "bd2119ee2bfb8c8d7c427e8af3c35d537534281e09e23013bca5b138", + "g": "a08a267e92456ba48e157dd7e77bdd35aba0fc50fe625a10a6a7fc5e", + "pv": "c6d963e8892916ab8753d3c342037cd122123c4dd783a07af21f8dac", + "pp": "a8e9f8f34fd631b1d5b9f41a90f4abc0d3935cea7baba0bb34c96f59" + }, + "script_hashes2": { + "m": "c0c671fba483641a71bb92d3a8b7c52c90bf1c01e2b83116ad7d4536", + "g": "1f3a4aa08cfa0e47fff200578f0d4847b6890b7093a765773feb35de", + "pv": "c0c671fba483641a71bb92d3a8b7c52c90bf1c01e2b83116ad7d4536", + "pp": "b4fd6dfe4a643aeec5d75dbb1f27198fc2aabf30bf92ed5470253792" + }, "datum_hashes1": { "m": "818ee3db3bbbd04f9f2ce21778cac3ac605802a4fcb00c8b3a58ee2dafc17d46", "g": "964af1ff2a66ce472d34ac39b47f356b6d971d62c794a89ec12825d5de30f3aa", diff --git a/tests/conftest.py b/tests/conftest.py index be41cf8f..092a801e 100755 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,10 +5,10 @@ def pytest_addoption(parser): parser.addoption( - "--local-url", action="store", default="http://127.0.0.1:8053/api/v0" + "--local-url", action="store", default="http://127.0.0.1:8053/api/v1" ) parser.addoption( - "--compare-url", action="store", default="https://guild.koios.rest/api/v0" + "--compare-url", action="store", default="https://guild.koios.rest/api/v1" ) parser.addoption( "--api-schema-file", diff --git a/tests/setup-tests.sh b/tests/setup-tests.sh index 1262ac58..6bd86044 100755 --- a/tests/setup-tests.sh +++ b/tests/setup-tests.sh @@ -13,13 +13,13 @@ cat <<-EOF To run the endpoint validation tests, use the below: schemathesis --pre-run not_empty_response run --request-timeout 5000 https://guild.koios.rest/koiosapi.yaml --hypothesis-phases=explicit \\ - --hypothesis-verbosity quiet -b http://127.0.0.1:8053/api/v0 -c all --validate-schema=true -H "Content-Type: application/json" + --hypothesis-verbosity quiet -b http://127.0.0.1:8053/api/v1 -c all --validate-schema=true -H "Content-Type: application/json" - where http://127.0.0.1:8053/api/v0 is the URL of instance you want to test, and guild.koios.rest is the target enviornment for testing. + where http://127.0.0.1:8053/api/v1 is the URL of instance you want to test, and guild.koios.rest is the target enviornment for testing. To run the data validations tests, use the below: - pytest --local-url http://127.0.0.1:8053/api/v0 --compare-url https://guild.koios.rest/api/v0 --api-schema-file ../specs/results/koiosapi-guild.yaml -x -v + pytest --local-url http://127.0.0.1:8053/api/v1 --compare-url https://guild.koios.rest/api/v1 --api-schema-file ../specs/results/koiosapi-guild.yaml -x -v Arguments: local-run : URL of instance you want to test" From ae651368de34d1bc1456d5df20e172e953ecb969 Mon Sep 17 00:00:00 2001 From: rdlrt <3169068+rdlrt@users.noreply.github.com> Date: Wed, 25 Oct 2023 16:26:13 +1100 Subject: [PATCH 84/86] Fix asset_utxos to return all utxos --- files/grest/rpc/assets/asset_utxos.sql | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/files/grest/rpc/assets/asset_utxos.sql b/files/grest/rpc/assets/asset_utxos.sql index d91d2785..d1a7b675 100644 --- a/files/grest/rpc/assets/asset_utxos.sql +++ b/files/grest/rpc/assets/asset_utxos.sql @@ -35,9 +35,17 @@ BEGIN RETURN QUERY WITH + _txo_list AS ( + SELECT + txo.id + FROM tx_out AS txo + INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txo.id + WHERE mto.ident = ANY(_asset_id_list) + AND txo.consumed_by_tx_in_id IS NULL + ), _assets AS ( SELECT - txo.id, + txol.id, JSONB_AGG(CASE WHEN ma.policy IS NULL THEN NULL ELSE JSONB_BUILD_OBJECT( 'policy_id', ENCODE(ma.policy, 'hex'), @@ -47,13 +55,11 @@ BEGIN 'quantity', mto.quantity::text ) END) as assets - FROM tx_out AS txo - INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txo.id + FROM _txo_list AS txol + INNER JOIN ma_tx_out AS mto ON mto.tx_out_id = txol.id LEFT JOIN multi_asset AS ma ON ma.id = mto.ident LEFT JOIN grest.asset_info_cache AS aic ON aic.asset_id = ma.id - WHERE mto.ident = ANY(_asset_id_list) - AND txo.consumed_by_tx_in_id IS NULL - GROUP BY txo.id + GROUP BY txol.id ) SELECT ENCODE(tx.hash, 'hex')::text AS tx_hash, From c0145bc800bde1f5d6e15456da1e01c4f2dbe20f Mon Sep 17 00:00:00 2001 From: rdlrt <3169068+rdlrt@users.noreply.github.com> Date: Thu, 26 Oct 2023 11:37:37 +1100 Subject: [PATCH 85/86] Fix Typo on script join for fetching UTxOs --- files/grest/rpc/account/account_utxos.sql | 2 +- files/grest/rpc/address/address_utxos.sql | 2 +- files/grest/rpc/address/credential_utxos.sql | 2 +- files/grest/rpc/assets/asset_utxos.sql | 2 +- files/grest/rpc/transactions/utxo_info.sql | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/files/grest/rpc/account/account_utxos.sql b/files/grest/rpc/account/account_utxos.sql index 5efa664e..acdcb63f 100644 --- a/files/grest/rpc/account/account_utxos.sql +++ b/files/grest/rpc/account/account_utxos.sql @@ -83,7 +83,7 @@ BEGIN LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id LEFT JOIN block AS b ON b.id = tx.block_id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id - LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN script ON script.id = tx_out.reference_script_id LEFT JOIN _assets ON tx_out.id = _assets.id WHERE tx_out.stake_address_id IN (SELECT sa.id FROM stake_address AS sa WHERE sa.view = ANY(_stake_addresses)) AND tx_out.consumed_by_tx_in_id IS NULL diff --git a/files/grest/rpc/address/address_utxos.sql b/files/grest/rpc/address/address_utxos.sql index e0a322d6..30f45519 100644 --- a/files/grest/rpc/address/address_utxos.sql +++ b/files/grest/rpc/address/address_utxos.sql @@ -83,7 +83,7 @@ BEGIN LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id LEFT JOIN block AS b ON b.id = tx.block_id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id - LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN script ON script.id = tx_out.reference_script_id LEFT JOIN _assets ON tx_out.id = _assets.id WHERE tx_out.address = ANY(_addresses) AND tx_out.consumed_by_tx_in_id IS NULL diff --git a/files/grest/rpc/address/credential_utxos.sql b/files/grest/rpc/address/credential_utxos.sql index 8d4d4520..f505103c 100644 --- a/files/grest/rpc/address/credential_utxos.sql +++ b/files/grest/rpc/address/credential_utxos.sql @@ -89,7 +89,7 @@ BEGIN LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id LEFT JOIN block AS b ON b.id = tx.block_id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id - LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN script ON script.id = tx_out.reference_script_id LEFT JOIN _assets ON tx_out.id = _assets.id WHERE tx_out.payment_cred = ANY(_payment_cred_bytea) AND tx_out.consumed_by_tx_in_id IS NULL diff --git a/files/grest/rpc/assets/asset_utxos.sql b/files/grest/rpc/assets/asset_utxos.sql index d1a7b675..975ed92d 100644 --- a/files/grest/rpc/assets/asset_utxos.sql +++ b/files/grest/rpc/assets/asset_utxos.sql @@ -103,7 +103,7 @@ BEGIN LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id LEFT JOIN block AS b ON b.id = tx.block_id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id - LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN script ON script.id = tx_out.reference_script_id WHERE tx_out.consumed_by_tx_in_id IS NULL ; END; diff --git a/files/grest/rpc/transactions/utxo_info.sql b/files/grest/rpc/transactions/utxo_info.sql index 22b9fb7f..50fe3277 100644 --- a/files/grest/rpc/transactions/utxo_info.sql +++ b/files/grest/rpc/transactions/utxo_info.sql @@ -102,7 +102,7 @@ BEGIN LEFT JOIN stake_address AS sa ON tx_out.stake_address_id = sa.id LEFT JOIN block AS b ON b.id = tx.block_id LEFT JOIN datum ON datum.id = tx_out.inline_datum_id - LEFT JOIN script ON script.tx_id = tx_out.reference_script_id + LEFT JOIN script ON script.id = tx_out.reference_script_id LEFT JOIN _assets ON tx_out.id = _assets.id WHERE tx_out.id = ANY(_tx_id_list) ; From 907503f8474f406ffc3f2fa56306f23388eda3ea Mon Sep 17 00:00:00 2001 From: Martin Lang <47434720+gitmachtl@users.noreply.github.com> Date: Thu, 26 Oct 2023 02:41:57 +0200 Subject: [PATCH 86/86] Update projects.json - Adding SPO Scripts to Community Tools (#243) Co-authored-by: RdLrT <3169068+rdlrt@users.noreply.github.com> --- projects.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/projects.json b/projects.json index ca105c5e..3c0e4c87 100644 --- a/projects.json +++ b/projects.json @@ -160,6 +160,13 @@ "light": "https://raw.githubusercontent.com/cardano-community/koios-artifacts/main/images/projects/raywallet.png" } }, + {"text": "Stakepool Operator Scripts", + "link": "https://github.com/gitmachtl/scripts", + "logo": { + "dark": "https://raw.githubusercontent.com/gitmachtl/cardano-related-stuff/master/sposcripts_logo_640x640.png", + "light": "https://raw.githubusercontent.com/gitmachtl/cardano-related-stuff/master/sposcripts_logo_640x640.png" + } + }, { "text": "Tabulali", "link": "https://eddex.github.io/tabulali/",

    5^;Vpe%7IW_|%)V76ckLFx;Oic(A7<&>0#_w9N{ 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 Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). - - # Problems solved by Koios - - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) - to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is - free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be - health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. - - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be - consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute - or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that - pass the health-check for the endpoint. - - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For - such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that - take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part - of Koios API. - - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants - automatically receive failover support, they reduce impact of any subset of clusters going through update process. - - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to - give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. - - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will - ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. - - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. - - # Architecture - - ## How does Koios work? - - ![High-Level architecture overview](/koios-design.png) - - We will go bottom to top (from builder's eyes to run through the above) briefly: - - - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. - - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. - - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. + Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. # API Usage @@ -91,7 +56,7 @@ info: When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. - The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. + The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Preferred: count=estimated"`. Sounds confusing? Let's see this in practice, to hopefully make it easier. Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    @@ -185,16 +150,16 @@ info: for experiments. ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. + All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). + Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. + There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. x-logo: - url: "https://api.koios.rest/koios.png" + url: "https://api.koios.rest/images/koios.png" servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 diff --git a/specs/results/koiosapi-mainnet.yaml b/specs/results/koiosapi-mainnet.yaml index 475aaefb..b665fc58 100644 --- a/specs/results/koiosapi-mainnet.yaml +++ b/specs/results/koiosapi-mainnet.yaml @@ -3,42 +3,7 @@ info: title: Koios API version: 1.0.10 description: | - Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. - - > Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). - - # Problems solved by Koios - - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) - to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is - free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be - health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. - - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be - consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute - or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that - pass the health-check for the endpoint. - - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For - such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that - take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part - of Koios API. - - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants - automatically receive failover support, they reduce impact of any subset of clusters going through update process. - - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to - give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. - - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will - ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. - - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. - - # Architecture - - ## How does Koios work? - - ![High-Level architecture overview](/koios-design.png) - - We will go bottom to top (from builder's eyes to run through the above) briefly: - - - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. - - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. - - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. + Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. # API Usage @@ -91,7 +56,7 @@ info: When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. - The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. + The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Preferred: count=estimated"`. Sounds confusing? Let's see this in practice, to hopefully make it easier. Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    @@ -185,16 +150,16 @@ info: for experiments. ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. + All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). + Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. + There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. x-logo: - url: "https://api.koios.rest/koios.png" + url: "https://api.koios.rest/images/koios.png" servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 diff --git a/specs/results/koiosapi-preprod.yaml b/specs/results/koiosapi-preprod.yaml index 92a07811..3439d4f2 100644 --- a/specs/results/koiosapi-preprod.yaml +++ b/specs/results/koiosapi-preprod.yaml @@ -3,42 +3,7 @@ info: title: Koios API version: 1.0.10 description: | - Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. - - > Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). - - # Problems solved by Koios - - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) - to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is - free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be - health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. - - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be - consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute - or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that - pass the health-check for the endpoint. - - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For - such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that - take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part - of Koios API. - - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants - automatically receive failover support, they reduce impact of any subset of clusters going through update process. - - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to - give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. - - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will - ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. - - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. - - # Architecture - - ## How does Koios work? - - ![High-Level architecture overview](/koios-design.png) - - We will go bottom to top (from builder's eyes to run through the above) briefly: - - - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. - - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. - - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. + Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. # API Usage @@ -91,7 +56,7 @@ info: When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. - The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. + The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Preferred: count=estimated"`. Sounds confusing? Let's see this in practice, to hopefully make it easier. Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    @@ -185,16 +150,16 @@ info: for experiments. ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. + All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). + Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. + There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. x-logo: - url: "https://api.koios.rest/koios.png" + url: "https://api.koios.rest/images/koios.png" servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 diff --git a/specs/results/koiosapi-preview.yaml b/specs/results/koiosapi-preview.yaml index 2bcbd159..1c6ad088 100644 --- a/specs/results/koiosapi-preview.yaml +++ b/specs/results/koiosapi-preview.yaml @@ -3,42 +3,7 @@ info: title: Koios API version: 1.0.10 description: | - Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. - - > Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). - - # Problems solved by Koios - - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) - to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is - free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be - health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. - - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be - consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute - or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that - pass the health-check for the endpoint. - - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For - such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that - take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part - of Koios API. - - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants - automatically receive failover support, they reduce impact of any subset of clusters going through update process. - - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to - give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. - - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will - ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. - - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. - - # Architecture - - ## How does Koios work? - - ![High-Level architecture overview](/koios-design.png) - - We will go bottom to top (from builder's eyes to run through the above) briefly: - - - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. - - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. - - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. + Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. # API Usage @@ -91,7 +56,7 @@ info: When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. - The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. + The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Preferred: count=estimated"`. Sounds confusing? Let's see this in practice, to hopefully make it easier. Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    @@ -185,16 +150,16 @@ info: for experiments. ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. + All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). + Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. + There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. x-logo: - url: "https://api.koios.rest/koios.png" + url: "https://api.koios.rest/images/koios.png" servers: - url: https://api.koios.rest/api/v0 - url: https://guild.koios.rest/api/v0 diff --git a/specs/templates/1-api-info.yaml b/specs/templates/1-api-info.yaml index 5351c7ae..43fd9438 100644 --- a/specs/templates/1-api-info.yaml +++ b/specs/templates/1-api-info.yaml @@ -2,42 +2,7 @@ info: title: Koios API version: 1.0.10 description: | - Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. - - > Note: While we've done sufficient ground work - we're still going through testing/learning/adapting phase based on feedback. Feel free to give it a go, but just remember it is not yet finalized for production consumption and will be refreshed weekly (Saturday 8am UTC). - - # Problems solved by Koios - - As the size of blockchain grows rapidly, we're looking at increasingly expensive resources and maintenance costs (financially as well as time-wise) - to maintain a scalable solution that will automatically failover and have health-checks, ensuring most synched versions are returned. With Koios, anyone is - free to either add their backend instance to the cluster, or use the query layer without running a node or cardano-db-sync instance themselves. There will be - health-checks for each endpoint to ensure that connections do not go to a dud backend with stale information. - - Moreover, folks who do put in tremendous amount of efforts to go through discovery phrase - are often ending up with local solutions, that may not be - consistent across the board (e.g. Live Stake queries across existing explorers). Since all the queries used by/for Koios layer is on GitHub, anyone can contribute - or leverage the query knowledge base, and help each other out while doing so. An additional endpoint added will only be load balanced between the servers that - pass the health-check for the endpoint. - - It is almost impossible to fetch some live data (for example, Live Stake against a pool) due to the cost of computation and amount of data on chain. For - such queries, many folks are already using different cache methods, or capturing ledger information from node. Wouldn't it be nice to have these crunched data that - take quite a few minutes to run be shared and available to be able to pick a relatively recent execution across the nodes? This will be available out of the box as part - of Koios API. - - There is also a worry when going through updates about feasibility/breaking changes/etc. that can become a bottleneck for providers. Since Koios participants - automatically receive failover support, they reduce impact of any subset of clusters going through update process. - - The lightweight query layers currently present are unfortunately closed source, centralised, and create a single point of failure. With Koios, our aim is to - give enough flexibility to all the participants to select their backend, or pick from any of the available ones instead. - - Bad human errors causing an outage? The bandwidth for Koios becomes better with more participation, but just in case there is not enough participation - we will - ensure that at least 4 trusted Koios instances across the globe will be around for the initial year, allowing for enough time for adoption to build up gradually. - - Flexibility to participate at different levels. A consumer of these services can participate with a complete independent instance (optionally extend existing ones), by running only certain parts (e.g. submit-api or PostgREST only), or simply consuming the API without running anything locally. - - # Architecture - - ## How does Koios work? - - ![High-Level architecture overview](/koios-design.png) - - We will go bottom to top (from builder's eyes to run through the above) briefly: - - - *Instance(s)* : These are essentially [PostgREST](https://postgrest.org/en/latest/) instances with the REST service attached to Postgres DB populated using [cardano-db-sync](https://cardano-community.github.io/guild-operators/Build/dbsync/). Every consumer who is providing their own instance will be expected to serve at least a PostgREST instance, as this is what allows us to string instances together after health-checks. If using guild-operator setup instructions, these will be provisioned for you by setup scripts. - - *Health-check Services* : These are lightweight [HAProxy](http://www.haproxy.org) instances that will be gatekeepers for individual endpoints, handling health-checks, sample data verification, etc. A builder _may_ opt-in to run this monitoring service, and add their instance to GitHub repository. Again, setting up HAProxy will be part of setup scripts on guild-operator's repo for those interested. - - *DNS Routing* : These will be the entry points from monitoring layer to trusted instances that will route to health-check proxy services. We will be using at least two DNS servers ourselves to not have single point of failure, but that does not limit users to elect any of the other server endpoints instead, since the API works right from the PostgREST layer itself. + Koios is best described as a Decentralized and Elastic RESTful query layer for exploring data on Cardano blockchain to consume within applications/wallets/explorers/etc. This page not only provides an OpenAPI Spec for live implementation, but also ability to execute live demo from client browser against each endpoint with pre-filled examples. # API Usage @@ -90,7 +55,7 @@ info: When you query any endpoint in PostgREST, the number of observations returned will be limited to a maximum of 1000 rows (set via `max-rows` config option in the `grest.conf` file. This - however - is a result of a paginated call, wherein the [ up to ] 1000 records you see without any parameters is the first page. If you want to see the next 1000 results, you can always append `offset=1000` to view the next set of results. But what if 1000 is too high for your use-case and you want smaller page? Well, you can specify a smaller limit using parameter `limit`, which will see shortly in an example below. The obvious question at this point that would cross your mind is - how do I know if I need to offset and what range I am querying? This is where headers come in to your aid. - The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Profile: count=estimated"`. + The default headers returned by PostgREST will include a `Content-Range` field giving a range of observations returned. For large tables, this range could include a wildcard `*` as it is expensive to query exact count of observations from endpoint. But if you would like to get an estimate count without overloading servers, PostgREST can utilise Postgres's own maintenance thread results (which maintain stats for each table) to provide you a count, by specifying a header `"Preferred: count=estimated"`. Sounds confusing? Let's see this in practice, to hopefully make it easier. Consider a simple case where I want query `blocks` endpoint for `block_height` column and focus on `content-range` header to monitor the rows we discussed above.

    @@ -184,13 +149,13 @@ info: for experiments. ### I am only interested in collaborating on queries, where can I find the code and how to collaborate? - All the Postgres codebase against db-sync instance is available on guild-operator's github repo [here](https://github.com/cardano-community/guild-operators/tree/alpha/files/grest/rpc). Feel free to raise an issue/PR to discuss anything related to those queries. + All the Postgres codebase against db-sync instance is available on [koios-artifacts](https://github.com/cardano-community/koios-artifacts/tree/main/files/grest/rpc) repo on github. Feel free to raise an issue/PR to discuss anything related to those queries. ### I am not sure how to set up an instance. Is there an easy start guide? - Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/joinchat/+zE4Lce_QUepiY2U1). + Yes, there is a setup script (expect you to read carefully the help section) and instructions [here](https://cardano-community.github.io/guild-operators/Build/grest/). Should you need any assistance, feel free to hop in to the [discussion group](https://t.me/CardanoKoios). ### Too much reading, I want to discuss in person - There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/+zE4Lce_QUepiY2U1) and start a discussion from there. + There are bi-weekly calls held that anyone is free to join - or you can drop in to the [telegram group](https://t.me/CardanoKoios) and start a discussion from there. x-logo: - url: "https://api.koios.rest/koios.png" + url: "https://api.koios.rest/images/koios.png" From 407b4eea9095f14c8db442512da881784bb69a8b Mon Sep 17 00:00:00 2001 From: rdlrt <3169068+rdlrt@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:39:39 +0000 Subject: [PATCH 71/86] Move eternl and tosidrop images to github --- images/projects/eternl-512.png | Bin 0 -> 64064 bytes images/projects/umbrella_blue.png | Bin 0 -> 110151 bytes projects.json | 8 ++++---- 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 images/projects/eternl-512.png create mode 100644 images/projects/umbrella_blue.png diff --git a/images/projects/eternl-512.png b/images/projects/eternl-512.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee60854dcf17e326a273a485e72c17dd91c4bcf GIT binary patch literal 64064 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
    fan<8OngqkqOPKE~c@!NcIsn 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�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