From 43f3be4924914c410cd99f908348301fa435e28a Mon Sep 17 00:00:00 2001 From: nym21 Date: Wed, 29 Apr 2026 16:51:01 +0200 Subject: [PATCH] global: fixes --- .../src/generators/javascript/api.rs | 12 +- .../src/generators/javascript/client.rs | 195 ++-- .../src/generators/python/client.rs | 42 +- .../brk_bindgen/src/generators/python/tree.rs | 2 +- .../brk_bindgen/src/generators/rust/client.rs | 19 + crates/brk_cli/README.md | 5 +- crates/brk_client/src/lib.rs | 19 + .../src/steps/preparer/tx_removal.rs | 2 - .../src/steps/rebuilder/linearize/mod.rs | 3 - .../brk_mempool/src/stores/entry_pool/mod.rs | 3 +- crates/brk_mempool/src/stores/tx_store.rs | 9 - crates/brk_query/src/impl/addr.rs | 9 +- crates/brk_query/src/impl/block/info.rs | 26 +- crates/brk_query/src/impl/block/txs.rs | 4 +- crates/brk_rpc/src/lib.rs | 6 +- crates/brk_rpc/src/methods.rs | 16 +- crates/brk_server/src/params/txids_param.rs | 8 +- crates/brk_types/src/addr_validation.rs | 5 +- modules/brk-client/index.js | 933 ++++++++++-------- packages/brk_client/brk_client/__init__.py | 824 ++++++++-------- packages/brk_client/pyproject.toml | 5 + .../addresses/test_address_info.py | 48 + .../addresses/test_address_txs.py | 49 + .../addresses/test_address_txs_chain.py | 49 + .../addresses/test_address_txs_mempool.py | 33 + .../addresses/test_address_utxo.py | 52 + .../addresses/test_validate_address.py | 53 + .../tests/mempool_compat/blocks/test_block.py | 12 + .../blocks/test_block_header.py | 13 + .../blocks/test_block_height.py | 13 + .../mempool_compat/blocks/test_block_raw.py | 13 + .../blocks/test_block_status.py | 12 + .../blocks/test_block_txid_index.py | 37 + .../mempool_compat/blocks/test_block_txids.py | 12 + .../mempool_compat/blocks/test_block_txs.py | 14 + .../blocks/test_block_txs_start.py | 68 ++ .../mempool_compat/blocks/test_block_v1.py | 22 + .../blocks/test_blocks_height.py | 14 + .../blocks/test_blocks_recent.py | 35 + .../blocks/test_blocks_tip_hash.py | 37 + .../blocks/test_blocks_tip_height.py | 32 + .../blocks/test_blocks_v1_height.py | 14 + .../blocks/test_blocks_v1_recent.py | 31 + .../tests/mempool_compat/check_endpoints.py | 158 +++ .../tests/mempool_compat/conftest.py | 216 ++++ .../fees/test_mempool_blocks.py | 27 + .../tests/mempool_compat/fees/test_precise.py | 42 + .../mempool_compat/fees/test_recommended.py | 33 + .../general/test_difficulty_adjustment.py | 35 + .../general/test_historical_price.py | 54 + .../mempool_compat/general/test_prices.py | 22 + .../mempool_compat/mempool/test_mempool.py | 23 + .../mempool_compat/mempool/test_recent.py | 25 + .../mempool_compat/mempool/test_txids.py | 46 + .../tests/mempool_compat/mining/conftest.py | 17 + .../mining/test_blocks_fee_rates.py | 15 + .../mempool_compat/mining/test_blocks_fees.py | 15 + .../mining/test_blocks_rewards.py | 15 + .../mining/test_blocks_sizes_weights.py | 15 + .../mining/test_blocks_timestamp.py | 15 + .../mining/test_difficulty_adjustments.py | 15 + .../mempool_compat/mining/test_hashrate.py | 15 + .../mining/test_hashrate_pools.py | 15 + .../tests/mempool_compat/mining/test_pool.py | 13 + .../mempool_compat/mining/test_pool_blocks.py | 15 + .../mining/test_pool_blocks_height.py | 15 + .../mining/test_pool_hashrate.py | 13 + .../tests/mempool_compat/mining/test_pools.py | 45 + .../mining/test_pools_period.py | 15 + .../mining/test_reward_stats.py | 15 + .../mempool_compat/transactions/test_cpfp.py | 12 + .../transactions/test_post_tx.py | 40 + .../transactions/test_transaction_times.py | 56 ++ .../mempool_compat/transactions/test_tx.py | 21 + .../transactions/test_tx_hex.py | 12 + .../transactions/test_tx_merkle_proof.py | 12 + .../transactions/test_tx_merkleblock_proof.py | 12 + .../transactions/test_tx_outspend.py | 38 + .../transactions/test_tx_outspends.py | 12 + .../transactions/test_tx_raw.py | 12 + .../transactions/test_tx_rbf.py | 16 + .../transactions/test_tx_status.py | 12 + packages/brk_client/tests/test_basic.py | 14 + packages/brk_client/tests/test_metric_data.py | 8 - packages/brk_client/uv.lock | 2 +- scripts/mempool_compat/conftest.py | 338 ------- scripts/mempool_compat/pyproject.toml | 11 - scripts/mempool_compat/test_addresses.py | 159 --- scripts/mempool_compat/test_blocks.py | 265 ----- scripts/mempool_compat/test_fees.py | 84 -- scripts/mempool_compat/test_general.py | 122 --- scripts/mempool_compat/test_mempool.py | 72 -- scripts/mempool_compat/test_mining.py | 239 ----- scripts/mempool_compat/test_transactions.py | 161 --- scripts/mempool_compat/uv.lock | 385 -------- website/scripts/_types.js | 1 - website/scripts/explorer/address.js | 13 +- website/scripts/explorer/block.js | 7 +- website/scripts/explorer/index.js | 6 +- website/scripts/utils/cache.js | 38 - website/scripts/utils/chart/index.js | 4 +- 101 files changed, 3074 insertions(+), 2869 deletions(-) create mode 100644 packages/brk_client/tests/mempool_compat/addresses/test_address_info.py create mode 100644 packages/brk_client/tests/mempool_compat/addresses/test_address_txs.py create mode 100644 packages/brk_client/tests/mempool_compat/addresses/test_address_txs_chain.py create mode 100644 packages/brk_client/tests/mempool_compat/addresses/test_address_txs_mempool.py create mode 100644 packages/brk_client/tests/mempool_compat/addresses/test_address_utxo.py create mode 100644 packages/brk_client/tests/mempool_compat/addresses/test_validate_address.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_header.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_height.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_raw.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_status.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_txid_index.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_txids.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_txs.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_txs_start.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_block_v1.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_blocks_height.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_blocks_recent.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_hash.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_height.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_height.py create mode 100644 packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_recent.py create mode 100644 packages/brk_client/tests/mempool_compat/check_endpoints.py create mode 100644 packages/brk_client/tests/mempool_compat/conftest.py create mode 100644 packages/brk_client/tests/mempool_compat/fees/test_mempool_blocks.py create mode 100644 packages/brk_client/tests/mempool_compat/fees/test_precise.py create mode 100644 packages/brk_client/tests/mempool_compat/fees/test_recommended.py create mode 100644 packages/brk_client/tests/mempool_compat/general/test_difficulty_adjustment.py create mode 100644 packages/brk_client/tests/mempool_compat/general/test_historical_price.py create mode 100644 packages/brk_client/tests/mempool_compat/general/test_prices.py create mode 100644 packages/brk_client/tests/mempool_compat/mempool/test_mempool.py create mode 100644 packages/brk_client/tests/mempool_compat/mempool/test_recent.py create mode 100644 packages/brk_client/tests/mempool_compat/mempool/test_txids.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/conftest.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_blocks_fee_rates.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_blocks_fees.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_blocks_rewards.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_blocks_sizes_weights.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_blocks_timestamp.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_difficulty_adjustments.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_hashrate.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_hashrate_pools.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_pool.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_pool_blocks.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_pool_blocks_height.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_pool_hashrate.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_pools.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_pools_period.py create mode 100644 packages/brk_client/tests/mempool_compat/mining/test_reward_stats.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_cpfp.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_post_tx.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_transaction_times.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_hex.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_merkle_proof.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_merkleblock_proof.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_outspend.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_outspends.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_raw.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_rbf.py create mode 100644 packages/brk_client/tests/mempool_compat/transactions/test_tx_status.py delete mode 100644 scripts/mempool_compat/conftest.py delete mode 100644 scripts/mempool_compat/pyproject.toml delete mode 100644 scripts/mempool_compat/test_addresses.py delete mode 100644 scripts/mempool_compat/test_blocks.py delete mode 100644 scripts/mempool_compat/test_fees.py delete mode 100644 scripts/mempool_compat/test_general.py delete mode 100644 scripts/mempool_compat/test_mempool.py delete mode 100644 scripts/mempool_compat/test_mining.py delete mode 100644 scripts/mempool_compat/test_transactions.py delete mode 100644 scripts/mempool_compat/uv.lock delete mode 100644 website/scripts/utils/cache.js diff --git a/crates/brk_bindgen/src/generators/javascript/api.rs b/crates/brk_bindgen/src/generators/javascript/api.rs index e9f45bb08..0e96b0726 100644 --- a/crates/brk_bindgen/src/generators/javascript/api.rs +++ b/crates/brk_bindgen/src/generators/javascript/api.rs @@ -69,7 +69,7 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { writeln!( output, - " * @param {{{{ signal?: AbortSignal, onUpdate?: (value: {}) => void }}}} [options]", + " * @param {{{{ signal?: AbortSignal, onValue?: (value: {}) => void }}}} [options]", return_type ) .unwrap(); @@ -78,18 +78,18 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { let params = build_method_params(endpoint); let params_with_opts = if params.is_empty() { - "{ signal, onUpdate } = {}".to_string() + "{ signal, onValue } = {}".to_string() } else { - format!("{}, {{ signal, onUpdate }} = {{}}", params) + format!("{}, {{ signal, onValue }} = {{}}", params) }; writeln!(output, " async {}({}) {{", method_name, params_with_opts).unwrap(); let path = build_path_template(&endpoint.path, &endpoint.path_params); let fetch_call = if endpoint.returns_json() { - "this.getJson(path, { signal, onUpdate })" + "this.getJson(path, { signal, onValue })" } else { - "this.getText(path, { signal, onUpdate })" + "this.getText(path, { signal, onValue })" }; if endpoint.query_params.is_empty() { @@ -126,7 +126,7 @@ pub fn generate_api_methods(output: &mut String, endpoints: &[Endpoint]) { if endpoint.supports_csv { writeln!( output, - " if (format === 'csv') return this.getText(path, {{ signal, onUpdate }});" + " if (format === 'csv') return this.getText(path, {{ signal, onValue }});" ) .unwrap(); } diff --git a/crates/brk_bindgen/src/generators/javascript/client.rs b/crates/brk_bindgen/src/generators/javascript/client.rs index 9c714eb76..728cf9b17 100644 --- a/crates/brk_bindgen/src/generators/javascript/client.rs +++ b/crates/brk_bindgen/src/generators/javascript/client.rs @@ -16,12 +16,16 @@ pub fn generate_base_client(output: &mut String) { * @typedef {{Object}} BrkClientOptions * @property {{string}} baseUrl - Base URL for the API * @property {{number}} [timeout] - Request timeout in milliseconds - * @property {{string|boolean}} [cache] - Enable browser cache with default name (true), custom name (string), or disable (false). No effect in Node.js. Default: true + * @property {{string|boolean}} [browserCache] - Enable browser Cache API with default name (true), custom name (string), or disable (false). No effect in Node.js. Default: true + * @property {{number|boolean}} [memCache] - In-memory parsed-response cache size (LRU). true/undefined → 1000, false/0 → disabled. Lets 304 responses skip the JSON parse entirely. Default: 1000 */ const _isBrowser = typeof window !== 'undefined' && 'caches' in window; const _runIdle = (/** @type {{VoidFunction}} */ fn) => (globalThis.requestIdleCallback ?? setTimeout)(fn); -const _defaultCacheName = '__BRK_CLIENT__'; +const _defaultBrowserCacheName = '__BRK_CLIENT__'; +const _DEFAULT_MEM_CACHE_SIZE = 1000; + +/** @template T @typedef {{{{ etag: string | null, value: T }}}} _MemEntry */ /** @param {{*}} v */ const _addCamelGetters = (v) => {{ if (Array.isArray(v)) {{ v.forEach(_addCamelGetters); return v; }} @@ -38,12 +42,12 @@ const _addCamelGetters = (v) => {{ }}; /** - * @param {{string|boolean|undefined}} cache + * @param {{string|boolean|undefined}} option * @returns {{Promise}} */ -const _openCache = (cache) => {{ - if (!_isBrowser || cache === false) return Promise.resolve(null); - const name = typeof cache === 'string' ? cache : _defaultCacheName; +const _openBrowserCache = (option) => {{ + if (!_isBrowser || option === false) return Promise.resolve(null); + const name = typeof option === 'string' ? option : _defaultBrowserCacheName; return caches.open(name).catch(() => null); }}; @@ -233,7 +237,7 @@ function _wrapSeriesData(raw) {{ * @property {{(n: number) => RangeBuilder}} first - Get first n items * @property {{(n: number) => RangeBuilder}} last - Get last n items * @property {{(n: number) => SkippedBuilder}} skip - Skip first n items, chain with take() - * @property {{(onUpdate?: (value: SeriesData) => void) => Promise>}} fetch - Fetch all data + * @property {{(onValue?: (value: SeriesData) => void) => Promise>}} fetch - Fetch all data * @property {{() => Promise}} fetchCsv - Fetch all data as CSV * @property {{() => Promise}} len - Get total number of data points * @property {{() => Promise}} version - Get the current version of the series @@ -249,7 +253,7 @@ function _wrapSeriesData(raw) {{ * @property {{(n: number) => DateRangeBuilder}} first - Get first n items * @property {{(n: number) => DateRangeBuilder}} last - Get last n items * @property {{(n: number) => DateSkippedBuilder}} skip - Skip first n items, chain with take() - * @property {{(onUpdate?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch all data + * @property {{(onValue?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch all data * @property {{() => Promise}} fetchCsv - Fetch all data as CSV * @property {{() => Promise}} len - Get total number of data points * @property {{() => Promise}} version - Get the current version of the series @@ -260,39 +264,39 @@ function _wrapSeriesData(raw) {{ /** @typedef {{SeriesEndpoint}} AnySeriesEndpoint */ /** @template T @typedef {{Object}} SingleItemBuilder - * @property {{(onUpdate?: (value: SeriesData) => void) => Promise>}} fetch - Fetch the item + * @property {{(onValue?: (value: SeriesData) => void) => Promise>}} fetch - Fetch the item * @property {{() => Promise}} fetchCsv - Fetch as CSV * @property {{Thenable}} then - Thenable */ /** @template T @typedef {{Object}} DateSingleItemBuilder - * @property {{(onUpdate?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch the item + * @property {{(onValue?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch the item * @property {{() => Promise}} fetchCsv - Fetch as CSV * @property {{DateThenable}} then - Thenable */ /** @template T @typedef {{Object}} SkippedBuilder * @property {{(n: number) => RangeBuilder}} take - Take n items after skipped position - * @property {{(onUpdate?: (value: SeriesData) => void) => Promise>}} fetch - Fetch from skipped position to end + * @property {{(onValue?: (value: SeriesData) => void) => Promise>}} fetch - Fetch from skipped position to end * @property {{() => Promise}} fetchCsv - Fetch as CSV * @property {{Thenable}} then - Thenable */ /** @template T @typedef {{Object}} DateSkippedBuilder * @property {{(n: number) => DateRangeBuilder}} take - Take n items after skipped position - * @property {{(onUpdate?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch from skipped position to end + * @property {{(onValue?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch from skipped position to end * @property {{() => Promise}} fetchCsv - Fetch as CSV * @property {{DateThenable}} then - Thenable */ /** @template T @typedef {{Object}} RangeBuilder - * @property {{(onUpdate?: (value: SeriesData) => void) => Promise>}} fetch - Fetch the range + * @property {{(onValue?: (value: SeriesData) => void) => Promise>}} fetch - Fetch the range * @property {{() => Promise}} fetchCsv - Fetch as CSV * @property {{Thenable}} then - Thenable */ /** @template T @typedef {{Object}} DateRangeBuilder - * @property {{(onUpdate?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch the range + * @property {{(onValue?: (value: DateSeriesData) => void) => Promise>}} fetch - Fetch the range * @property {{() => Promise}} fetchCsv - Fetch as CSV * @property {{DateThenable}} then - Thenable */ @@ -340,7 +344,7 @@ function _endpoint(client, name, index) {{ * @returns {{DateRangeBuilder}} */ const rangeBuilder = (start, end) => ({{ - fetch(onUpdate) {{ return client._fetchSeriesData(buildPath(start, end), onUpdate); }}, + fetch(onValue) {{ return client._fetchSeriesData(buildPath(start, end), onValue); }}, fetchCsv() {{ return client.getText(buildPath(start, end, 'csv')); }}, then(resolve, reject) {{ return this.fetch().then(resolve, reject); }}, }}); @@ -350,7 +354,7 @@ function _endpoint(client, name, index) {{ * @returns {{DateSingleItemBuilder}} */ const singleItemBuilder = (idx) => ({{ - fetch(onUpdate) {{ return client._fetchSeriesData(buildPath(idx, idx + 1), onUpdate); }}, + fetch(onValue) {{ return client._fetchSeriesData(buildPath(idx, idx + 1), onValue); }}, fetchCsv() {{ return client.getText(buildPath(idx, idx + 1, 'csv')); }}, then(resolve, reject) {{ return this.fetch().then(resolve, reject); }}, }}); @@ -361,7 +365,7 @@ function _endpoint(client, name, index) {{ */ const skippedBuilder = (start) => ({{ take(n) {{ return rangeBuilder(start, start + n); }}, - fetch(onUpdate) {{ return client._fetchSeriesData(buildPath(start, undefined), onUpdate); }}, + fetch(onValue) {{ return client._fetchSeriesData(buildPath(start, undefined), onValue); }}, fetchCsv() {{ return client.getText(buildPath(start, undefined, 'csv')); }}, then(resolve, reject) {{ return this.fetch().then(resolve, reject); }}, }}); @@ -377,7 +381,7 @@ function _endpoint(client, name, index) {{ first(n) {{ return rangeBuilder(undefined, n); }}, last(n) {{ return n === 0 ? rangeBuilder(undefined, 0) : rangeBuilder(-n, undefined); }}, skip(n) {{ return skippedBuilder(n); }}, - fetch(onUpdate) {{ return client._fetchSeriesData(buildPath(), onUpdate); }}, + fetch(onValue) {{ return client._fetchSeriesData(buildPath(), onValue); }}, fetchCsv() {{ return client.getText(buildPath(undefined, undefined, 'csv')); }}, len() {{ return client.getSeriesLen(name, index); }}, version() {{ return client.getSeriesVersion(name, index); }}, @@ -401,10 +405,45 @@ class BrkClientBase {{ this.baseUrl = rawUrl.endsWith('/') ? rawUrl.slice(0, -1) : rawUrl; this.timeout = isString ? 5000 : (options.timeout ?? 5000); /** @type {{Promise}} */ - this._cachePromise = _openCache(isString ? undefined : options.cache); + this._browserCachePromise = _openBrowserCache(isString ? undefined : options.browserCache); /** @type {{Cache | null}} */ - this._cache = null; - this._cachePromise.then(c => this._cache = c); + this._browserCache = null; + this._browserCachePromise.then(c => this._browserCache = c); + const memOpt = isString ? undefined : options.memCache; + this._memCacheMax = memOpt === false || memOpt === 0 + ? 0 + : (typeof memOpt === 'number' ? memOpt : _DEFAULT_MEM_CACHE_SIZE); + /** @type {{Map>}} */ + this._memCache = new Map(); + }} + + /** + * @template T + * @param {{string}} key + * @returns {{_MemEntry | undefined}} + */ + _memGet(key) {{ + if (!this._memCacheMax) return undefined; + const hit = this._memCache.get(key); + if (!hit) return undefined; + this._memCache.delete(key); + this._memCache.set(key, hit); + return /** @type {{_MemEntry}} */ (hit); + }} + + /** + * @param {{string}} key + * @param {{string | null}} etag + * @param {{unknown}} value + */ + _memSet(key, etag, value) {{ + if (!this._memCacheMax) return; + if (this._memCache.has(key)) this._memCache.delete(key); + else if (this._memCache.size >= this._memCacheMax) {{ + const oldest = this._memCache.keys().next().value; + if (oldest !== undefined) this._memCache.delete(oldest); + }} + this._memCache.set(key, {{ etag, value }}); }} /** @@ -422,66 +461,86 @@ class BrkClientBase {{ }} /** - * Make a GET request - races cache vs network, first to resolve calls onUpdate. - * Shared implementation backing `getJson` and `getText`. + * Make a GET request with layered caching. + * + * Contract: + * - The returned Promise resolves with the **freshest** value (post-revalidation). + * - `onValue` fires once with the freshest value, or twice if a stale snapshot + * could be shown first (stale-while-revalidate). On a 304 there is no second fire. + * + * Layers: + * - L1 (memCache): in-memory parsed values keyed by URL+ETag. Lets 304s skip the parse entirely. + * - L2 (browserCache): Cache API, survives reload and feeds onValue fast on cold start. + * * @template T * @param {{string}} path * @param {{(res: Response) => Promise}} parse - Response body reader - * @param {{{{ onUpdate?: (value: T) => void, signal?: AbortSignal }}}} [options] + * @param {{{{ onValue?: (value: T) => void, signal?: AbortSignal }}}} [options] * @returns {{Promise}} */ - async _getCached(path, parse, {{ onUpdate, signal }} = {{}}) {{ + async _getCached(path, parse, {{ onValue, signal }} = {{}}) {{ const url = `${{this.baseUrl}}${{path}}`; - const cache = this._cache ?? await this._cachePromise; + /** @type {{_MemEntry | undefined}} */ + const memHit = this._memGet(url); + const browserCache = this._browserCache ?? await this._browserCachePromise; - let resolved = false; - /** @type {{Response | null}} */ - let cachedRes = null; - - // Race cache vs network - first to resolve calls onUpdate - const cachePromise = cache?.match(url).then(async (res) => {{ - cachedRes = res ?? null; - if (!res) return null; - const value = await parse(res); - if (!resolved && onUpdate) {{ - resolved = true; - onUpdate(value); - }} - return value; - }}); - - const networkPromise = this.get(path, {{ signal }}).then(async (res) => {{ - const cloned = res.clone(); - const value = await parse(res); - // Skip update if ETag matches and cache already delivered - if (cachedRes?.headers.get('ETag') === res.headers.get('ETag')) {{ - if (!resolved && onUpdate) {{ - resolved = true; - onUpdate(value); - }} + // L1 fast path: deliver from memCache, revalidate via network. + // ETag match → zero parse, zero clone, zero cache write, no second onValue fire. + if (memHit) {{ + if (onValue) onValue(memHit.value); + try {{ + const res = await this.get(path, {{ signal }}); + const netEtag = res.headers.get('ETag'); + if (netEtag && netEtag === memHit.etag) return memHit.value; + const cloned = browserCache ? res.clone() : null; + const value = await parse(res); + this._memSet(url, netEtag, value); + if (onValue) onValue(value); + if (cloned) _runIdle(() => browserCache.put(url, cloned)); return value; + }} catch {{ + return memHit.value; }} - resolved = true; - if (onUpdate) onUpdate(value); - if (cache) _runIdle(() => cache.put(url, cloned)); - return value; - }}); + }} + + // L1 miss: race browserCache (stale snapshot) vs network (fresh). + let networkSettled = false; + const stalePromise = onValue && browserCache + ? browserCache.match(url).then(async (res) => {{ + if (!res || networkSettled) return null; + const value = await parse(res); + if (networkSettled) return value; + this._memSet(url, res.headers.get('ETag'), value); + onValue(value); + return value; + }}).catch(() => null) + : null; try {{ - return await networkPromise; + const res = await this.get(path, {{ signal }}); + networkSettled = true; + const netEtag = res.headers.get('ETag'); + // Stale won and populated memCache with matching ETag → reuse, skip parse + second onValue. + const populated = /** @type {{_MemEntry | undefined}} */ (this._memGet(url)); + if (populated && netEtag && netEtag === populated.etag) return populated.value; + const cloned = browserCache ? res.clone() : null; + const value = await parse(res); + this._memSet(url, netEtag, value); + if (onValue) onValue(value); + if (cloned) _runIdle(() => browserCache.put(url, cloned)); + return value; }} catch (e) {{ - // Network failed - wait for cache - const cachedValue = await cachePromise?.catch(() => null); - if (cachedValue != null) return cachedValue; + const stale = await stalePromise; + if (stale != null) return stale; throw e; }} }} /** - * Make a GET request expecting a JSON response. Cached and supports `onUpdate`. + * Make a GET request expecting a JSON response. Cached and supports `onValue`. * @template T * @param {{string}} path - * @param {{{{ onUpdate?: (value: T) => void, signal?: AbortSignal }}}} [options] + * @param {{{{ onValue?: (value: T) => void, signal?: AbortSignal }}}} [options] * @returns {{Promise}} */ getJson(path, options) {{ @@ -490,9 +549,9 @@ class BrkClientBase {{ /** * Make a GET request expecting a text response (text/plain, text/csv, ...). - * Cached and supports `onUpdate`, same as `getJson`. + * Cached and supports `onValue`, same as `getJson`. * @param {{string}} path - * @param {{{{ onUpdate?: (value: string) => void, signal?: AbortSignal }}}} [options] + * @param {{{{ onValue?: (value: string) => void, signal?: AbortSignal }}}} [options] * @returns {{Promise}} */ getText(path, options) {{ @@ -503,12 +562,12 @@ class BrkClientBase {{ * Fetch series data and wrap with helper methods (internal) * @template T * @param {{string}} path - * @param {{(value: DateSeriesData) => void}} [onUpdate] + * @param {{(value: DateSeriesData) => void}} [onValue] * @returns {{Promise>}} */ - async _fetchSeriesData(path, onUpdate) {{ - const wrappedOnUpdate = onUpdate ? (/** @type {{SeriesData}} */ raw) => onUpdate(_wrapSeriesData(raw)) : undefined; - const raw = await this.getJson(path, {{ onUpdate: wrappedOnUpdate }}); + async _fetchSeriesData(path, onValue) {{ + const wrappedOnValue = onValue ? (/** @type {{SeriesData}} */ raw) => onValue(_wrapSeriesData(raw)) : undefined; + const raw = await this.getJson(path, {{ onValue: wrappedOnValue }}); return _wrapSeriesData(raw); }} }} diff --git a/crates/brk_bindgen/src/generators/python/client.rs b/crates/brk_bindgen/src/generators/python/client.rs index 3e5e85361..48562429c 100644 --- a/crates/brk_bindgen/src/generators/python/client.rs +++ b/crates/brk_bindgen/src/generators/python/client.rs @@ -325,13 +325,13 @@ AnyDateSeriesData = DateSeriesData[Any] class _EndpointConfig: """Shared endpoint configuration.""" - client: BrkClientBase + client: BrkClient name: str index: Index start: Optional[int] end: Optional[int] - def __init__(self, client: BrkClientBase, name: str, index: Index, + def __init__(self, client: BrkClient, name: str, index: Index, start: Optional[int] = None, end: Optional[int] = None): self.client = client self.name = name @@ -366,6 +366,12 @@ class _EndpointConfig: def get_csv(self) -> str: return self.client.get_text(self._build_path(format='csv')) + def get_len(self) -> int: + return self.client.get_series_len(self.name, self.index) + + def get_version(self) -> Version: + return self.client.get_series_version(self.name, self.index) + class RangeBuilder(Generic[T]): """Builder with range specified.""" @@ -449,7 +455,7 @@ class SeriesEndpoint(Generic[T]): data = endpoint.skip(100).take(10).fetch() """ - def __init__(self, client: BrkClientBase, name: str, index: Index): + def __init__(self, client: BrkClient, name: str, index: Index): self._config = _EndpointConfig(client, name, index) @overload @@ -483,6 +489,14 @@ class SeriesEndpoint(Generic[T]): """Fetch all data as CSV.""" return self._config.get_csv() + def len(self) -> int: + """Total number of data points for this series.""" + return self._config.get_len() + + def version(self) -> Version: + """Current version of the series.""" + return self._config.get_version() + def path(self) -> str: """Get the base endpoint path.""" return self._config.path() @@ -500,7 +514,7 @@ class DateSeriesEndpoint(Generic[T]): data = endpoint[:10].fetch() """ - def __init__(self, client: BrkClientBase, name: str, index: Index): + def __init__(self, client: BrkClient, name: str, index: Index): self._config = _EndpointConfig(client, name, index) @overload @@ -546,6 +560,14 @@ class DateSeriesEndpoint(Generic[T]): """Fetch all data as CSV.""" return self._config.get_csv() + def len(self) -> int: + """Total number of data points for this series.""" + return self._config.get_len() + + def version(self) -> Version: + """Current version of the series.""" + return self._config.get_version() + def path(self) -> str: """Get the base endpoint path.""" return self._config.path() @@ -604,10 +626,10 @@ pub fn generate_index_accessors(output: &mut String, patterns: &[IndexSetPattern // Generate helper functions writeln!( output, - r#"def _ep(c: BrkClientBase, n: str, i: Index) -> SeriesEndpoint[Any]: + r#"def _ep(c: BrkClient, n: str, i: Index) -> SeriesEndpoint[Any]: return SeriesEndpoint(c, n, i) -def _dep(c: BrkClientBase, n: str, i: Index) -> DateSeriesEndpoint[Any]: +def _dep(c: BrkClient, n: str, i: Index) -> DateSeriesEndpoint[Any]: return DateSeriesEndpoint(c, n, i) "# ) @@ -623,7 +645,7 @@ def _dep(c: BrkClientBase, n: str, i: Index) -> DateSeriesEndpoint[Any]: writeln!(output, "class {}(Generic[T]):", by_class_name).unwrap(); writeln!( output, - " def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n" + " def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n" ) .unwrap(); for index in &pattern.indexes { @@ -648,7 +670,7 @@ def _dep(c: BrkClientBase, n: str, i: Index) -> DateSeriesEndpoint[Any]: writeln!(output, " by: {}[T]", by_class_name).unwrap(); writeln!( output, - " def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, {}(c, n)", + " def __init__(self, c: BrkClient, n: str): self._n, self.by = n, {}(c, n)", by_class_name ) .unwrap(); @@ -705,13 +727,13 @@ pub fn generate_structural_patterns( if pattern.is_templated() { writeln!( output, - " def __init__(self, client: BrkClientBase, acc: str, disc: str):" + " def __init__(self, client: BrkClient, acc: str, disc: str):" ) .unwrap(); } else { writeln!( output, - " def __init__(self, client: BrkClientBase, acc: str):" + " def __init__(self, client: BrkClient, acc: str):" ) .unwrap(); } diff --git a/crates/brk_bindgen/src/generators/python/tree.rs b/crates/brk_bindgen/src/generators/python/tree.rs index 582de8dd5..dd464f736 100644 --- a/crates/brk_bindgen/src/generators/python/tree.rs +++ b/crates/brk_bindgen/src/generators/python/tree.rs @@ -64,7 +64,7 @@ fn generate_tree_class( writeln!(output, " ").unwrap(); writeln!( output, - " def __init__(self, client: BrkClientBase, base_path: str = ''):" + " def __init__(self, client: BrkClient, base_path: str = ''):" ) .unwrap(); diff --git a/crates/brk_bindgen/src/generators/rust/client.rs b/crates/brk_bindgen/src/generators/rust/client.rs index 72c72dac7..f4b6cf542 100644 --- a/crates/brk_bindgen/src/generators/rust/client.rs +++ b/crates/brk_bindgen/src/generators/rust/client.rs @@ -187,6 +187,14 @@ impl EndpointConfig {{ fn get_text(&self, format: Option<&str>) -> Result {{ self.client.get_text(&self.build_path(format)) }} + + fn get_len(&self) -> Result {{ + self.client.get_json(&format!("/api/series/{{}}/{{}}/len", self.name, self.index.name())) + }} + + fn get_version(&self) -> Result {{ + self.client.get_json(&format!("/api/series/{{}}/{{}}/version", self.name, self.index.name())) + }} }} /// Builder for series endpoint queries. @@ -280,6 +288,17 @@ impl SeriesEndpoint {{ self.config.get_text(Some("csv")) }} + /// Total number of data points for this series. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> Result {{ + self.config.get_len() + }} + + /// Current version of the series. + pub fn version(&self) -> Result {{ + self.config.get_version() + }} + /// Get the base endpoint path. pub fn path(&self) -> String {{ self.config.path() diff --git a/crates/brk_cli/README.md b/crates/brk_cli/README.md index 8ba6e06bb..37af914b7 100644 --- a/crates/brk_cli/README.md +++ b/crates/brk_cli/README.md @@ -19,10 +19,11 @@ BRK uses [sparse files](https://en.wikipedia.org/wiki/Sparse_file). Tools like ` ## Install ```bash -rustup update -RUSTFLAGS="-C target-cpu=native" cargo install --locked brk_cli +rustup update && RUSTFLAGS="-C target-cpu=native" cargo install --locked brk_cli --version $(cargo search brk_cli | head -1 | awk -F'"' '{print $2}') ``` +Updates Rust, then builds `brk_cli` with optimizations tuned to your CPU. The `--version $(...)` subshell queries crates.io for the absolute latest published version, including pre-releases (rc/beta/alpha); without it, `cargo install` only picks the latest stable. + Portable build (without native CPU optimizations): ```bash diff --git a/crates/brk_client/src/lib.rs b/crates/brk_client/src/lib.rs index 1ad1d9b08..77e465d50 100644 --- a/crates/brk_client/src/lib.rs +++ b/crates/brk_client/src/lib.rs @@ -159,6 +159,14 @@ impl EndpointConfig { fn get_text(&self, format: Option<&str>) -> Result { self.client.get_text(&self.build_path(format)) } + + fn get_len(&self) -> Result { + self.client.get_json(&format!("/api/series/{}/{}/len", self.name, self.index.name())) + } + + fn get_version(&self) -> Result { + self.client.get_json(&format!("/api/series/{}/{}/version", self.name, self.index.name())) + } } /// Builder for series endpoint queries. @@ -252,6 +260,17 @@ impl SeriesEndpoint { self.config.get_text(Some("csv")) } + /// Total number of data points for this series. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> Result { + self.config.get_len() + } + + /// Current version of the series. + pub fn version(&self) -> Result { + self.config.get_version() + } + /// Get the base endpoint path. pub fn path(&self) -> String { self.config.path() diff --git a/crates/brk_mempool/src/steps/preparer/tx_removal.rs b/crates/brk_mempool/src/steps/preparer/tx_removal.rs index 81c8ed44c..d82e1aae1 100644 --- a/crates/brk_mempool/src/steps/preparer/tx_removal.rs +++ b/crates/brk_mempool/src/steps/preparer/tx_removal.rs @@ -42,8 +42,6 @@ impl TxRemoval { .collect() } - /// `Replaced` if any of `tx`'s inputs is now claimed by a freshly - /// added tx (BIP-125 inferred); otherwise `Vanished`. fn find_removal(tx: &Transaction, spent_by: &SpentBy) -> Self { tx.input .iter() diff --git a/crates/brk_mempool/src/steps/rebuilder/linearize/mod.rs b/crates/brk_mempool/src/steps/rebuilder/linearize/mod.rs index ca50df0f3..22f3f96d6 100644 --- a/crates/brk_mempool/src/steps/rebuilder/linearize/mod.rs +++ b/crates/brk_mempool/src/steps/rebuilder/linearize/mod.rs @@ -43,9 +43,6 @@ impl Linearizer { .collect() } - /// Singleton clusters bypass SFL: there's only one ordering. Larger - /// clusters are linearized into chunks, each chunk becoming a Package - /// with its order index recorded for downstream stability. fn pack_cluster(cluster: &Cluster, cluster_id: u32) -> Vec { if cluster.nodes.len() == 1 { return vec![Package::singleton(cluster, cluster_id)]; diff --git a/crates/brk_mempool/src/stores/entry_pool/mod.rs b/crates/brk_mempool/src/stores/entry_pool/mod.rs index 52d2d7062..004eb80c8 100644 --- a/crates/brk_mempool/src/stores/entry_pool/mod.rs +++ b/crates/brk_mempool/src/stores/entry_pool/mod.rs @@ -45,8 +45,7 @@ impl EntryPool { } /// Direct children of a transaction (txs whose `depends` includes - /// `prefix`). Derived on demand via a linear scan, called only by - /// the CPFP query endpoint, which is not on the hot path. + /// `prefix`). Linear scan over all entries. pub fn children(&self, prefix: &TxidPrefix) -> SmallVec<[TxidPrefix; 2]> { self.entries .iter() diff --git a/crates/brk_mempool/src/stores/tx_store.rs b/crates/brk_mempool/src/stores/tx_store.rs index 6e4952d8a..33244740e 100644 --- a/crates/brk_mempool/src/stores/tx_store.rs +++ b/crates/brk_mempool/src/stores/tx_store.rs @@ -36,16 +36,12 @@ impl TxStore { self.promote_recent(new_recent); } - /// Append to the cap-bounded sample buffer if there's room. The - /// pre-cap window becomes the next `recent()` value. fn sample_recent(buf: &mut Vec, txid: &Txid, tx: &Transaction) { if buf.len() < RECENT_CAP { buf.push(MempoolRecentTx::from((txid, tx))); } } - /// Record `txid` in the unresolved set if any input lacks a - /// prevout. Cleared later by `apply_fills` once all inputs fill. fn track_unresolved(&mut self, txid: &Txid, tx: &Transaction) { if tx.input.iter().any(|i| i.prevout.is_none()) { self.unresolved.insert(txid.clone()); @@ -96,9 +92,6 @@ impl TxStore { applied } - /// Apply each `(vin, prevout)` to its empty input slot. Skips vins - /// that are out of range or already filled. Returns the prevouts - /// that were actually written. fn write_prevouts(tx: &mut Transaction, fills: Vec<(Vin, TxOut)>) -> Vec { let mut applied = Vec::with_capacity(fills.len()); for (vin, prevout) in fills { @@ -118,8 +111,6 @@ impl TxStore { tx.total_sigop_cost = tx.total_sigop_cost(); } - /// Drop `txid` from the unresolved set if every input now has a - /// prevout. Idempotent if the tx was removed between phases. fn refresh_unresolved(&mut self, txid: &Txid) { if self.txs.get(txid).is_some_and(Self::all_resolved) { self.unresolved.remove(txid); diff --git a/crates/brk_query/src/impl/addr.rs b/crates/brk_query/src/impl/addr.rs index ca106b560..0244b4193 100644 --- a/crates/brk_query/src/impl/addr.rs +++ b/crates/brk_query/src/impl/addr.rs @@ -85,9 +85,12 @@ impl Query { tx_count: addr_data.tx_count, realized_price, }, - mempool_stats: self - .mempool() - .and_then(|m| m.addrs().get(&bytes).map(|e| e.stats.clone())), + mempool_stats: self.mempool().map(|m| { + m.addrs() + .get(&bytes) + .map(|e| e.stats.clone()) + .unwrap_or_default() + }), }) } diff --git a/crates/brk_query/src/impl/block/info.rs b/crates/brk_query/src/impl/block/info.rs index 9ce5bba19..9eeaf45a4 100644 --- a/crates/brk_query/src/impl/block/info.rs +++ b/crates/brk_query/src/impl/block/info.rs @@ -279,6 +279,7 @@ impl Query { coinbase_signature, coinbase_signature_ascii, scriptsig_bytes, + coinbase_total_size, ) = match reader.reader_at(positions[i]) { Ok(mut blk) => { let mut header_buf = [0u8; HEADER_SIZE]; @@ -291,6 +292,7 @@ impl Query { String::new(), String::new(), vec![], + 0, ) } else { // Skip tx count varint @@ -299,7 +301,7 @@ impl Query { let coinbase = Self::parse_coinbase_from_read(blk); ( header_buf, coinbase.0, coinbase.1, coinbase.2, coinbase.3, coinbase.4, - coinbase.5, + coinbase.5, coinbase.6, ) } } @@ -311,6 +313,7 @@ impl Query { String::new(), String::new(), vec![], + 0, ), }; let header = Self::decode_header(&raw_header)?; @@ -382,8 +385,11 @@ impl Query { coinbase_addresses, coinbase_signature, coinbase_signature_ascii, - avg_tx_size: if tx_count > 0 { - size as f64 / tx_count as f64 + avg_tx_size: if tx_count > 0 && coinbase_total_size > 0 { + let non_coinbase_total = (size as usize) + .saturating_sub(HEADER_SIZE + varint_len + coinbase_total_size); + let raw = non_coinbase_total as f64 / tx_count as f64; + (raw * 100.0).round() / 100.0 } else { 0.0 }, @@ -542,7 +548,15 @@ impl Query { fn parse_coinbase_from_read( reader: impl Read, - ) -> (String, Option, Vec, String, String, Vec) { + ) -> ( + String, + Option, + Vec, + String, + String, + Vec, + usize, + ) { let empty = ( String::new(), None, @@ -550,6 +564,7 @@ impl Query { String::new(), String::new(), vec![], + 0, ); let tx = @@ -558,6 +573,8 @@ impl Query { Err(_) => return empty, }; + let coinbase_total_size = tx.total_size(); + let scriptsig_bytes: Vec = tx .input .first() @@ -595,6 +612,7 @@ impl Query { coinbase_signature, coinbase_signature_ascii, scriptsig_bytes, + coinbase_total_size, ) } } diff --git a/crates/brk_query/src/impl/block/txs.rs b/crates/brk_query/src/impl/block/txs.rs index 678e09c56..583d02211 100644 --- a/crates/brk_query/src/impl/block/txs.rs +++ b/crates/brk_query/src/impl/block/txs.rs @@ -23,7 +23,9 @@ impl Query { let (first, tx_count) = self.block_tx_range(height)?; let start: usize = start_index.into(); if start >= tx_count { - return Ok(Vec::new()); + return Err(Error::OutOfRange( + "start index past last transaction in block".into(), + )); } let count = BLOCK_TXS_PAGE_SIZE.min(tx_count - start); let indices: Vec = (first + start..first + start + count) diff --git a/crates/brk_rpc/src/lib.rs b/crates/brk_rpc/src/lib.rs index 26a6928f7..8b3a394a7 100644 --- a/crates/brk_rpc/src/lib.rs +++ b/crates/brk_rpc/src/lib.rs @@ -62,11 +62,7 @@ pub enum Auth { CookieFile(PathBuf), } -/// -/// Bitcoin Core RPC Client -/// -/// Thread safe and free to clone -/// +/// Bitcoin Core RPC client. Thread-safe and cheap to clone. #[derive(Debug, Clone)] pub struct Client(pub(crate) Arc); diff --git a/crates/brk_rpc/src/methods.rs b/crates/brk_rpc/src/methods.rs index c7774c818..15b3a12f1 100644 --- a/crates/brk_rpc/src/methods.rs +++ b/crates/brk_rpc/src/methods.rs @@ -106,7 +106,6 @@ impl Client { }) } - /// Get block hash at a given height pub fn get_block_hash(&self, height: H) -> Result where H: Into + Copy, @@ -188,7 +187,6 @@ impl Client { Ok(FeeRate::from(r.mempool_min_fee * 100_000.0)) } - /// Get txids of all transactions in a memory pool pub fn get_raw_mempool(&self) -> Result> { let r: GetRawMempool = self.0.call_with_retry("getrawmempool", &[])?; r.0.iter() @@ -310,7 +308,19 @@ impl Client { pub fn send_raw_transaction(&self, hex: &str) -> Result { let txid: bitcoin::Txid = self .0 - .call_once("sendrawtransaction", &[Value::String(hex.to_string())])?; + .call_once("sendrawtransaction", &[Value::String(hex.to_string())]) + .map_err(|e| { + // Bitcoin Core returns RPC error codes for client-side problems + // (decode failed, verification failed, already in chain, etc.). + // Surface these as 400 (Parse) so HTTP callers see a 4xx, matching + // mempool.space's POST /api/tx behavior. + if let Error::CorepcRPC(JsonRpcError::Rpc(rpc)) = &e + && matches!(rpc.code, -22 | -25 | -26 | -27) + { + return Error::Parse(rpc.message.clone()); + } + e + })?; Ok(Txid::from(txid)) } diff --git a/crates/brk_server/src/params/txids_param.rs b/crates/brk_server/src/params/txids_param.rs index c472d2536..1294711df 100644 --- a/crates/brk_server/src/params/txids_param.rs +++ b/crates/brk_server/src/params/txids_param.rs @@ -18,7 +18,7 @@ impl TxidsParam { /// Rejects unknown keys to prevent cache-busting via injected query params. pub fn from_query(query: &str) -> Result { if query.is_empty() { - return Ok(Self { txids: Vec::new() }); + return Err("missing required query parameter `txId[]`".into()); } let mut txids = Vec::new(); for pair in query.split('&') { @@ -49,8 +49,7 @@ mod tests { const T2: &str = "0000000000000000000000000000000000000000000000000000000000000002"; #[test] - fn parses_empty_single_and_multi() { - assert!(TxidsParam::from_query("").unwrap().txids.is_empty()); + fn parses_single_and_multi() { assert_eq!(TxidsParam::from_query(&format!("txId[]={T1}")).unwrap().txids.len(), 1); assert_eq!( TxidsParam::from_query(&format!("txId%5B%5D={T1}&txId[]={T2}")) @@ -62,7 +61,8 @@ mod tests { } #[test] - fn rejects_unknown_key_and_invalid_txid() { + fn rejects_empty_unknown_key_and_invalid_txid() { + assert!(TxidsParam::from_query("").is_err()); assert!(TxidsParam::from_query("foo=bar").is_err()); assert!(TxidsParam::from_query("txId[]=notahex").is_err()); assert!(TxidsParam::from_query("noequals").is_err()); diff --git a/crates/brk_types/src/addr_validation.rs b/crates/brk_types/src/addr_validation.rs index 42dbaf838..55d5c6d06 100644 --- a/crates/brk_types/src/addr_validation.rs +++ b/crates/brk_types/src/addr_validation.rs @@ -72,7 +72,10 @@ impl AddrValidation { let output_type = OutputType::from(&script); let script_hex = script.as_bytes().to_lower_hex_string(); - let is_script = matches!(output_type, OutputType::P2SH | OutputType::P2TR); + let is_script = matches!( + output_type, + OutputType::P2SH | OutputType::P2WSH | OutputType::P2TR + ); let is_witness = matches!( output_type, OutputType::P2WPKH | OutputType::P2WSH | OutputType::P2TR | OutputType::P2A diff --git a/modules/brk-client/index.js b/modules/brk-client/index.js index 274ff069b..3b5d7aaf5 100644 --- a/modules/brk-client/index.js +++ b/modules/brk-client/index.js @@ -1295,12 +1295,16 @@ replaced it. Omitted on the root of an RBF response. * @typedef {Object} BrkClientOptions * @property {string} baseUrl - Base URL for the API * @property {number} [timeout] - Request timeout in milliseconds - * @property {string|boolean} [cache] - Enable browser cache with default name (true), custom name (string), or disable (false). No effect in Node.js. Default: true + * @property {string|boolean} [browserCache] - Enable browser Cache API with default name (true), custom name (string), or disable (false). No effect in Node.js. Default: true + * @property {number|boolean} [memCache] - In-memory parsed-response cache size (LRU). true/undefined → 1000, false/0 → disabled. Lets 304 responses skip the JSON parse entirely. Default: 1000 */ const _isBrowser = typeof window !== 'undefined' && 'caches' in window; const _runIdle = (/** @type {VoidFunction} */ fn) => (globalThis.requestIdleCallback ?? setTimeout)(fn); -const _defaultCacheName = '__BRK_CLIENT__'; +const _defaultBrowserCacheName = '__BRK_CLIENT__'; +const _DEFAULT_MEM_CACHE_SIZE = 1000; + +/** @template T @typedef {{ etag: string | null, value: T }} _MemEntry */ /** @param {*} v */ const _addCamelGetters = (v) => { if (Array.isArray(v)) { v.forEach(_addCamelGetters); return v; } @@ -1317,12 +1321,12 @@ const _addCamelGetters = (v) => { }; /** - * @param {string|boolean|undefined} cache + * @param {string|boolean|undefined} option * @returns {Promise} */ -const _openCache = (cache) => { - if (!_isBrowser || cache === false) return Promise.resolve(null); - const name = typeof cache === 'string' ? cache : _defaultCacheName; +const _openBrowserCache = (option) => { + if (!_isBrowser || option === false) return Promise.resolve(null); + const name = typeof option === 'string' ? option : _defaultBrowserCacheName; return caches.open(name).catch(() => null); }; @@ -1512,7 +1516,7 @@ function _wrapSeriesData(raw) { * @property {(n: number) => RangeBuilder} first - Get first n items * @property {(n: number) => RangeBuilder} last - Get last n items * @property {(n: number) => SkippedBuilder} skip - Skip first n items, chain with take() - * @property {(onUpdate?: (value: SeriesData) => void) => Promise>} fetch - Fetch all data + * @property {(onValue?: (value: SeriesData) => void) => Promise>} fetch - Fetch all data * @property {() => Promise} fetchCsv - Fetch all data as CSV * @property {() => Promise} len - Get total number of data points * @property {() => Promise} version - Get the current version of the series @@ -1528,7 +1532,7 @@ function _wrapSeriesData(raw) { * @property {(n: number) => DateRangeBuilder} first - Get first n items * @property {(n: number) => DateRangeBuilder} last - Get last n items * @property {(n: number) => DateSkippedBuilder} skip - Skip first n items, chain with take() - * @property {(onUpdate?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch all data + * @property {(onValue?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch all data * @property {() => Promise} fetchCsv - Fetch all data as CSV * @property {() => Promise} len - Get total number of data points * @property {() => Promise} version - Get the current version of the series @@ -1539,39 +1543,39 @@ function _wrapSeriesData(raw) { /** @typedef {SeriesEndpoint} AnySeriesEndpoint */ /** @template T @typedef {Object} SingleItemBuilder - * @property {(onUpdate?: (value: SeriesData) => void) => Promise>} fetch - Fetch the item + * @property {(onValue?: (value: SeriesData) => void) => Promise>} fetch - Fetch the item * @property {() => Promise} fetchCsv - Fetch as CSV * @property {Thenable} then - Thenable */ /** @template T @typedef {Object} DateSingleItemBuilder - * @property {(onUpdate?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch the item + * @property {(onValue?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch the item * @property {() => Promise} fetchCsv - Fetch as CSV * @property {DateThenable} then - Thenable */ /** @template T @typedef {Object} SkippedBuilder * @property {(n: number) => RangeBuilder} take - Take n items after skipped position - * @property {(onUpdate?: (value: SeriesData) => void) => Promise>} fetch - Fetch from skipped position to end + * @property {(onValue?: (value: SeriesData) => void) => Promise>} fetch - Fetch from skipped position to end * @property {() => Promise} fetchCsv - Fetch as CSV * @property {Thenable} then - Thenable */ /** @template T @typedef {Object} DateSkippedBuilder * @property {(n: number) => DateRangeBuilder} take - Take n items after skipped position - * @property {(onUpdate?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch from skipped position to end + * @property {(onValue?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch from skipped position to end * @property {() => Promise} fetchCsv - Fetch as CSV * @property {DateThenable} then - Thenable */ /** @template T @typedef {Object} RangeBuilder - * @property {(onUpdate?: (value: SeriesData) => void) => Promise>} fetch - Fetch the range + * @property {(onValue?: (value: SeriesData) => void) => Promise>} fetch - Fetch the range * @property {() => Promise} fetchCsv - Fetch as CSV * @property {Thenable} then - Thenable */ /** @template T @typedef {Object} DateRangeBuilder - * @property {(onUpdate?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch the range + * @property {(onValue?: (value: DateSeriesData) => void) => Promise>} fetch - Fetch the range * @property {() => Promise} fetchCsv - Fetch as CSV * @property {DateThenable} then - Thenable */ @@ -1619,7 +1623,7 @@ function _endpoint(client, name, index) { * @returns {DateRangeBuilder} */ const rangeBuilder = (start, end) => ({ - fetch(onUpdate) { return client._fetchSeriesData(buildPath(start, end), onUpdate); }, + fetch(onValue) { return client._fetchSeriesData(buildPath(start, end), onValue); }, fetchCsv() { return client.getText(buildPath(start, end, 'csv')); }, then(resolve, reject) { return this.fetch().then(resolve, reject); }, }); @@ -1629,7 +1633,7 @@ function _endpoint(client, name, index) { * @returns {DateSingleItemBuilder} */ const singleItemBuilder = (idx) => ({ - fetch(onUpdate) { return client._fetchSeriesData(buildPath(idx, idx + 1), onUpdate); }, + fetch(onValue) { return client._fetchSeriesData(buildPath(idx, idx + 1), onValue); }, fetchCsv() { return client.getText(buildPath(idx, idx + 1, 'csv')); }, then(resolve, reject) { return this.fetch().then(resolve, reject); }, }); @@ -1640,7 +1644,7 @@ function _endpoint(client, name, index) { */ const skippedBuilder = (start) => ({ take(n) { return rangeBuilder(start, start + n); }, - fetch(onUpdate) { return client._fetchSeriesData(buildPath(start, undefined), onUpdate); }, + fetch(onValue) { return client._fetchSeriesData(buildPath(start, undefined), onValue); }, fetchCsv() { return client.getText(buildPath(start, undefined, 'csv')); }, then(resolve, reject) { return this.fetch().then(resolve, reject); }, }); @@ -1656,7 +1660,7 @@ function _endpoint(client, name, index) { first(n) { return rangeBuilder(undefined, n); }, last(n) { return n === 0 ? rangeBuilder(undefined, 0) : rangeBuilder(-n, undefined); }, skip(n) { return skippedBuilder(n); }, - fetch(onUpdate) { return client._fetchSeriesData(buildPath(), onUpdate); }, + fetch(onValue) { return client._fetchSeriesData(buildPath(), onValue); }, fetchCsv() { return client.getText(buildPath(undefined, undefined, 'csv')); }, len() { return client.getSeriesLen(name, index); }, version() { return client.getSeriesVersion(name, index); }, @@ -1680,10 +1684,45 @@ class BrkClientBase { this.baseUrl = rawUrl.endsWith('/') ? rawUrl.slice(0, -1) : rawUrl; this.timeout = isString ? 5000 : (options.timeout ?? 5000); /** @type {Promise} */ - this._cachePromise = _openCache(isString ? undefined : options.cache); + this._browserCachePromise = _openBrowserCache(isString ? undefined : options.browserCache); /** @type {Cache | null} */ - this._cache = null; - this._cachePromise.then(c => this._cache = c); + this._browserCache = null; + this._browserCachePromise.then(c => this._browserCache = c); + const memOpt = isString ? undefined : options.memCache; + this._memCacheMax = memOpt === false || memOpt === 0 + ? 0 + : (typeof memOpt === 'number' ? memOpt : _DEFAULT_MEM_CACHE_SIZE); + /** @type {Map>} */ + this._memCache = new Map(); + } + + /** + * @template T + * @param {string} key + * @returns {_MemEntry | undefined} + */ + _memGet(key) { + if (!this._memCacheMax) return undefined; + const hit = this._memCache.get(key); + if (!hit) return undefined; + this._memCache.delete(key); + this._memCache.set(key, hit); + return /** @type {_MemEntry} */ (hit); + } + + /** + * @param {string} key + * @param {string | null} etag + * @param {unknown} value + */ + _memSet(key, etag, value) { + if (!this._memCacheMax) return; + if (this._memCache.has(key)) this._memCache.delete(key); + else if (this._memCache.size >= this._memCacheMax) { + const oldest = this._memCache.keys().next().value; + if (oldest !== undefined) this._memCache.delete(oldest); + } + this._memCache.set(key, { etag, value }); } /** @@ -1701,66 +1740,86 @@ class BrkClientBase { } /** - * Make a GET request - races cache vs network, first to resolve calls onUpdate. - * Shared implementation backing `getJson` and `getText`. + * Make a GET request with layered caching. + * + * Contract: + * - The returned Promise resolves with the **freshest** value (post-revalidation). + * - `onValue` fires once with the freshest value, or twice if a stale snapshot + * could be shown first (stale-while-revalidate). On a 304 there is no second fire. + * + * Layers: + * - L1 (memCache): in-memory parsed values keyed by URL+ETag. Lets 304s skip the parse entirely. + * - L2 (browserCache): Cache API, survives reload and feeds onValue fast on cold start. + * * @template T * @param {string} path * @param {(res: Response) => Promise} parse - Response body reader - * @param {{ onUpdate?: (value: T) => void, signal?: AbortSignal }} [options] + * @param {{ onValue?: (value: T) => void, signal?: AbortSignal }} [options] * @returns {Promise} */ - async _getCached(path, parse, { onUpdate, signal } = {}) { + async _getCached(path, parse, { onValue, signal } = {}) { const url = `${this.baseUrl}${path}`; - const cache = this._cache ?? await this._cachePromise; + /** @type {_MemEntry | undefined} */ + const memHit = this._memGet(url); + const browserCache = this._browserCache ?? await this._browserCachePromise; - let resolved = false; - /** @type {Response | null} */ - let cachedRes = null; - - // Race cache vs network - first to resolve calls onUpdate - const cachePromise = cache?.match(url).then(async (res) => { - cachedRes = res ?? null; - if (!res) return null; - const value = await parse(res); - if (!resolved && onUpdate) { - resolved = true; - onUpdate(value); - } - return value; - }); - - const networkPromise = this.get(path, { signal }).then(async (res) => { - const cloned = res.clone(); - const value = await parse(res); - // Skip update if ETag matches and cache already delivered - if (cachedRes?.headers.get('ETag') === res.headers.get('ETag')) { - if (!resolved && onUpdate) { - resolved = true; - onUpdate(value); - } + // L1 fast path: deliver from memCache, revalidate via network. + // ETag match → zero parse, zero clone, zero cache write, no second onValue fire. + if (memHit) { + if (onValue) onValue(memHit.value); + try { + const res = await this.get(path, { signal }); + const netEtag = res.headers.get('ETag'); + if (netEtag && netEtag === memHit.etag) return memHit.value; + const cloned = browserCache ? res.clone() : null; + const value = await parse(res); + this._memSet(url, netEtag, value); + if (onValue) onValue(value); + if (cloned) _runIdle(() => browserCache.put(url, cloned)); return value; + } catch { + return memHit.value; } - resolved = true; - if (onUpdate) onUpdate(value); - if (cache) _runIdle(() => cache.put(url, cloned)); - return value; - }); + } + + // L1 miss: race browserCache (stale snapshot) vs network (fresh). + let networkSettled = false; + const stalePromise = onValue && browserCache + ? browserCache.match(url).then(async (res) => { + if (!res || networkSettled) return null; + const value = await parse(res); + if (networkSettled) return value; + this._memSet(url, res.headers.get('ETag'), value); + onValue(value); + return value; + }).catch(() => null) + : null; try { - return await networkPromise; + const res = await this.get(path, { signal }); + networkSettled = true; + const netEtag = res.headers.get('ETag'); + // Stale won and populated memCache with matching ETag → reuse, skip parse + second onValue. + const populated = /** @type {_MemEntry | undefined} */ (this._memGet(url)); + if (populated && netEtag && netEtag === populated.etag) return populated.value; + const cloned = browserCache ? res.clone() : null; + const value = await parse(res); + this._memSet(url, netEtag, value); + if (onValue) onValue(value); + if (cloned) _runIdle(() => browserCache.put(url, cloned)); + return value; } catch (e) { - // Network failed - wait for cache - const cachedValue = await cachePromise?.catch(() => null); - if (cachedValue != null) return cachedValue; + const stale = await stalePromise; + if (stale != null) return stale; throw e; } } /** - * Make a GET request expecting a JSON response. Cached and supports `onUpdate`. + * Make a GET request expecting a JSON response. Cached and supports `onValue`. * @template T * @param {string} path - * @param {{ onUpdate?: (value: T) => void, signal?: AbortSignal }} [options] + * @param {{ onValue?: (value: T) => void, signal?: AbortSignal }} [options] * @returns {Promise} */ getJson(path, options) { @@ -1769,9 +1828,9 @@ class BrkClientBase { /** * Make a GET request expecting a text response (text/plain, text/csv, ...). - * Cached and supports `onUpdate`, same as `getJson`. + * Cached and supports `onValue`, same as `getJson`. * @param {string} path - * @param {{ onUpdate?: (value: string) => void, signal?: AbortSignal }} [options] + * @param {{ onValue?: (value: string) => void, signal?: AbortSignal }} [options] * @returns {Promise} */ getText(path, options) { @@ -1782,12 +1841,12 @@ class BrkClientBase { * Fetch series data and wrap with helper methods (internal) * @template T * @param {string} path - * @param {(value: DateSeriesData) => void} [onUpdate] + * @param {(value: DateSeriesData) => void} [onValue] * @returns {Promise>} */ - async _fetchSeriesData(path, onUpdate) { - const wrappedOnUpdate = onUpdate ? (/** @type {SeriesData} */ raw) => onUpdate(_wrapSeriesData(raw)) : undefined; - const raw = await this.getJson(path, { onUpdate: wrappedOnUpdate }); + async _fetchSeriesData(path, onValue) { + const wrappedOnValue = onValue ? (/** @type {SeriesData} */ raw) => onValue(_wrapSeriesData(raw)) : undefined; + const raw = await this.getJson(path, { onValue: wrappedOnValue }); return _wrapSeriesData(raw); } } @@ -2006,7 +2065,7 @@ function createSeriesPattern35(client, name) { return /** @type {SeriesPattern35 /** * Create a Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern} */ @@ -2072,7 +2131,7 @@ function createPct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65 /** * Create a AllEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AllEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern} */ @@ -2112,7 +2171,7 @@ function createAllEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPatte /** * Create a _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_10y1m1w1y2y3m3y4y5y6m6y8yPattern2} */ @@ -2151,7 +2210,7 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, acc) { /** * Create a _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_10y1m1w1y2y3m3y4y5y6m6y8yPattern3} */ @@ -2222,7 +2281,7 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, acc) { /** * Create a EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2} */ @@ -2260,7 +2319,7 @@ function createEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2 /** * Create a AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern} */ @@ -2297,7 +2356,7 @@ function createAverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern( /** * Create a EmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {EmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2} */ @@ -2336,7 +2395,7 @@ function createEmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2(c /** * Create a AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern pattern node * @template T - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern} */ @@ -2386,7 +2445,7 @@ function createAverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern(c /** * Create a IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern} */ @@ -2420,7 +2479,7 @@ function createIndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, acc) { /** * Create a AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6} */ @@ -2453,7 +2512,7 @@ function createAllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6(client, acc) { /** * Create a AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5} */ @@ -2486,7 +2545,7 @@ function createAllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5(client, acc) { /** * Create a AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4} */ @@ -2519,7 +2578,7 @@ function createAllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4(client, acc) { /** * Create a AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7} */ @@ -2552,7 +2611,7 @@ function createAllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7(client, acc) { /** * Create a AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern} */ @@ -2585,7 +2644,7 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) { /** * Create a CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2} */ @@ -2629,7 +2688,7 @@ function createCapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2(client /** * Create a Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} */ @@ -2659,7 +2718,7 @@ function createPct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern(client, acc) { /** * Create a _10y2y3y4y5y6y8yPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_10y2y3y4y5y6y8yPattern} */ @@ -2688,7 +2747,7 @@ function create_10y2y3y4y5y6y8yPattern(client, acc) { /** * Create a _1m1w1y24hBpsPercentRatioPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hBpsPercentRatioPattern} */ @@ -2728,7 +2787,7 @@ function create_1m1w1y24hBpsPercentRatioPattern(client, acc) { /** * Create a CapLossMvrvNetPriceProfitSoprPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CapLossMvrvNetPriceProfitSoprPattern} */ @@ -2757,7 +2816,7 @@ function createCapLossMvrvNetPriceProfitSoprPattern(client, acc) { /** * Create a InMaxMinPerSupplyPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {InMaxMinPerSupplyPattern} */ @@ -2786,7 +2845,7 @@ function createInMaxMinPerSupplyPattern(client, acc) { /** * Create a MaxMedianMinPct10Pct25Pct75Pct90Pattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {MaxMedianMinPct10Pct25Pct75Pct90Pattern2} */ @@ -2817,7 +2876,7 @@ function createMaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, acc) { /** * Create a MaxMedianMinPct10Pct25Pct75Pct90Pattern pattern node * @template T - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {MaxMedianMinPct10Pct25Pct75Pct90Pattern} */ @@ -2845,7 +2904,7 @@ function createMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc) { /** * Create a _1m1w1y2y4yAllPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y2y4yAllPattern} */ @@ -2872,7 +2931,7 @@ function create_1m1w1y2y4yAllPattern(client, acc) { /** * Create a ActivityAddrOutputsRealizedSupplyUnrealizedPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {ActivityAddrOutputsRealizedSupplyUnrealizedPattern} */ @@ -2899,7 +2958,7 @@ function createActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, acc) { /** * Create a AverageBlockCumulativeInSumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AverageBlockCumulativeInSumPattern} */ @@ -2926,7 +2985,7 @@ function createAverageBlockCumulativeInSumPattern(client, acc) { /** * Create a BpsCentsPercentilesRatioSatsUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsCentsPercentilesRatioSatsUsdPattern} */ @@ -2953,7 +3012,7 @@ function createBpsCentsPercentilesRatioSatsUsdPattern(client, acc) { /** * Create a CentsNegativeToUsdPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsNegativeToUsdPattern2} */ @@ -2980,7 +3039,7 @@ function createCentsNegativeToUsdPattern2(client, acc) { /** * Create a DeltaDominanceHalfInTotalPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {DeltaDominanceHalfInTotalPattern2} */ @@ -3007,7 +3066,7 @@ function createDeltaDominanceHalfInTotalPattern2(client, acc) { /** * Create a DeltaDominanceHalfInTotalPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {DeltaDominanceHalfInTotalPattern} */ @@ -3033,7 +3092,7 @@ function createDeltaDominanceHalfInTotalPattern(client, acc) { /** * Create a _1m1w1y24hBlockPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hBlockPattern2} */ @@ -3058,7 +3117,7 @@ function create_1m1w1y24hBlockPattern2(client, acc) { /** * Create a _1m1w1y24hBlockPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hBlockPattern} */ @@ -3083,7 +3142,7 @@ function create_1m1w1y24hBlockPattern(client, acc) { /** * Create a ActiveBidirectionalReactivatedReceivingSendingPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {ActiveBidirectionalReactivatedReceivingSendingPattern} */ @@ -3108,7 +3167,7 @@ function createActiveBidirectionalReactivatedReceivingSendingPattern(client, acc /** * Create a ActivityOutputsRealizedSupplyUnrealizedPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {ActivityOutputsRealizedSupplyUnrealizedPattern} */ @@ -3133,7 +3192,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern(client, acc) { /** * Create a ActivityOutputsRealizedSupplyUnrealizedPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {ActivityOutputsRealizedSupplyUnrealizedPattern3} */ @@ -3158,7 +3217,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern3(client, acc) { /** * Create a ActivityOutputsRealizedSupplyUnrealizedPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {ActivityOutputsRealizedSupplyUnrealizedPattern2} */ @@ -3183,7 +3242,7 @@ function createActivityOutputsRealizedSupplyUnrealizedPattern2(client, acc) { /** * Create a BlockChangeCumulativeDeltaSumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BlockChangeCumulativeDeltaSumPattern} */ @@ -3208,7 +3267,7 @@ function createBlockChangeCumulativeDeltaSumPattern(client, acc) { /** * Create a BpsCentsRatioSatsUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsCentsRatioSatsUsdPattern} */ @@ -3233,7 +3292,7 @@ function createBpsCentsRatioSatsUsdPattern(client, acc) { /** * Create a BtcCentsDeltaSatsUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BtcCentsDeltaSatsUsdPattern} */ @@ -3258,7 +3317,7 @@ function createBtcCentsDeltaSatsUsdPattern(client, acc) { /** * Create a BtcCentsSatsShareUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BtcCentsSatsShareUsdPattern} */ @@ -3283,7 +3342,7 @@ function createBtcCentsSatsShareUsdPattern(client, acc) { /** * Create a CapLossMvrvPriceProfitPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CapLossMvrvPriceProfitPattern} */ @@ -3308,7 +3367,7 @@ function createCapLossMvrvPriceProfitPattern(client, acc) { /** * Create a CentsToUsdPattern4 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsToUsdPattern4} */ @@ -3342,7 +3401,7 @@ function createCentsToUsdPattern4(client, acc) { /** * Create a PhsReboundThsPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {PhsReboundThsPattern} */ @@ -3366,7 +3425,7 @@ function createPhsReboundThsPattern(client, acc) { /** * Create a _1m1w1y24hPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern2} */ @@ -3389,7 +3448,7 @@ function create_1m1w1y24hPattern2(client, acc) { /** * Create a _1m1w1y24hPattern8 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern8} */ @@ -3412,7 +3471,7 @@ function create_1m1w1y24hPattern8(client, acc) { /** * Create a _1m1w1y24hPattern4 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern4} */ @@ -3435,7 +3494,7 @@ function create_1m1w1y24hPattern4(client, acc) { /** * Create a _1m1w1y24hPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern3} */ @@ -3458,7 +3517,7 @@ function create_1m1w1y24hPattern3(client, acc) { /** * Create a _1m1w1y24hPattern7 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern7} */ @@ -3481,7 +3540,7 @@ function create_1m1w1y24hPattern7(client, acc) { /** * Create a _1m1w1y2wPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y2wPattern} */ @@ -3504,7 +3563,7 @@ function create_1m1w1y2wPattern(client, acc) { /** * Create a _1m1w1y24hPattern5 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern5} */ @@ -3527,7 +3586,7 @@ function create_1m1w1y24hPattern5(client, acc) { /** * Create a _1m1w1y24hPattern6 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern6} */ @@ -3558,7 +3617,7 @@ function create_1m1w1y24hPattern6(client, acc) { /** * Create a AverageBlockCumulativeSumPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AverageBlockCumulativeSumPattern2} */ @@ -3581,7 +3640,7 @@ function createAverageBlockCumulativeSumPattern2(client, acc) { /** * Create a AverageBlockCumulativeSumPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AverageBlockCumulativeSumPattern3} */ @@ -3604,7 +3663,7 @@ function createAverageBlockCumulativeSumPattern3(client, acc) { /** * Create a BlockCumulativeNegativeSumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BlockCumulativeNegativeSumPattern} */ @@ -3627,7 +3686,7 @@ function createBlockCumulativeNegativeSumPattern(client, acc) { /** * Create a BlockCumulativeDeltaSumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BlockCumulativeDeltaSumPattern} */ @@ -3650,7 +3709,7 @@ function createBlockCumulativeDeltaSumPattern(client, acc) { /** * Create a BtcCentsSatsUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BtcCentsSatsUsdPattern} */ @@ -3673,7 +3732,7 @@ function createBtcCentsSatsUsdPattern(client, acc) { /** * Create a BtcCentsSatsUsdPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BtcCentsSatsUsdPattern2} */ @@ -3696,7 +3755,7 @@ function createBtcCentsSatsUsdPattern2(client, acc) { /** * Create a BtcCentsSatsUsdPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BtcCentsSatsUsdPattern3} */ @@ -3719,7 +3778,7 @@ function createBtcCentsSatsUsdPattern3(client, acc) { /** * Create a CentsDeltaToUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsDeltaToUsdPattern} */ @@ -3742,7 +3801,7 @@ function createCentsDeltaToUsdPattern(client, acc) { /** * Create a CentsToUsdPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsToUsdPattern3} */ @@ -3765,7 +3824,7 @@ function createCentsToUsdPattern3(client, acc) { /** * Create a CoindaysCoinyearsDormancyTransferPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CoindaysCoinyearsDormancyTransferPattern} */ @@ -3788,7 +3847,7 @@ function createCoindaysCoinyearsDormancyTransferPattern(client, acc) { /** * Create a LossNetNuplProfitPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {LossNetNuplProfitPattern} */ @@ -3811,7 +3870,7 @@ function createLossNetNuplProfitPattern(client, acc) { /** * Create a NuplRealizedSupplyUnrealizedPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {NuplRealizedSupplyUnrealizedPattern} */ @@ -3836,7 +3895,7 @@ function createNuplRealizedSupplyUnrealizedPattern(client, acc) { /** * Create a _1m1w1y24hPattern pattern node * @template T - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_1m1w1y24hPattern} */ @@ -3861,7 +3920,7 @@ function create_1m1w1y24hPattern(client, acc) { /** * Create a AverageBlockCumulativeSumPattern pattern node * @template T - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AverageBlockCumulativeSumPattern} */ @@ -3883,7 +3942,7 @@ function createAverageBlockCumulativeSumPattern(client, acc) { /** * Create a AdjustedRatioValuePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AdjustedRatioValuePattern} */ @@ -3904,7 +3963,7 @@ function createAdjustedRatioValuePattern(client, acc) { /** * Create a BlockCumulativeSumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BlockCumulativeSumPattern} */ @@ -3925,7 +3984,7 @@ function createBlockCumulativeSumPattern(client, acc) { /** * Create a BlocksDominanceRewardsPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BlocksDominanceRewardsPattern} */ @@ -3946,7 +4005,7 @@ function createBlocksDominanceRewardsPattern(client, acc) { /** * Create a BpsPercentRatioPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsPercentRatioPattern2} */ @@ -3967,7 +4026,7 @@ function createBpsPercentRatioPattern2(client, acc) { /** * Create a BpsPercentRatioPattern4 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsPercentRatioPattern4} */ @@ -3988,7 +4047,7 @@ function createBpsPercentRatioPattern4(client, acc) { /** * Create a BpsPriceRatioPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @param {string} disc - Discriminator suffix * @returns {BpsPriceRatioPattern} @@ -4010,7 +4069,7 @@ function createBpsPriceRatioPattern(client, acc, disc) { /** * Create a BpsPercentRatioPattern5 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsPercentRatioPattern5} */ @@ -4031,7 +4090,7 @@ function createBpsPercentRatioPattern5(client, acc) { /** * Create a BpsPercentRatioPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsPercentRatioPattern} */ @@ -4052,7 +4111,7 @@ function createBpsPercentRatioPattern(client, acc) { /** * Create a CentsSatsUsdPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsSatsUsdPattern3} */ @@ -4073,7 +4132,7 @@ function createCentsSatsUsdPattern3(client, acc) { /** * Create a CentsDeltaUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsDeltaUsdPattern} */ @@ -4094,7 +4153,7 @@ function createCentsDeltaUsdPattern(client, acc) { /** * Create a CentsNegativeUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsNegativeUsdPattern} */ @@ -4115,7 +4174,7 @@ function createCentsNegativeUsdPattern(client, acc) { /** * Create a CentsSatsUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsSatsUsdPattern} */ @@ -4143,7 +4202,7 @@ function createCentsSatsUsdPattern(client, acc) { /** * Create a CumulativeRollingSumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CumulativeRollingSumPattern} */ @@ -4164,7 +4223,7 @@ function createCumulativeRollingSumPattern(client, acc) { /** * Create a DeltaDominanceTotalPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {DeltaDominanceTotalPattern} */ @@ -4185,7 +4244,7 @@ function createDeltaDominanceTotalPattern(client, acc) { /** * Create a GreedNetPainPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {GreedNetPainPattern} */ @@ -4206,7 +4265,7 @@ function createGreedNetPainPattern(client, acc) { /** * Create a LossNuplProfitPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {LossNuplProfitPattern} */ @@ -4227,7 +4286,7 @@ function createLossNuplProfitPattern(client, acc) { /** * Create a RatioTransferValuePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {RatioTransferValuePattern} */ @@ -4248,7 +4307,7 @@ function createRatioTransferValuePattern(client, acc) { /** * Create a RsiStochPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @param {string} disc - Discriminator suffix * @returns {RsiStochPattern} @@ -4270,7 +4329,7 @@ function createRsiStochPattern(client, acc, disc) { /** * Create a SpendingSpentUnspentPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {SpendingSpentUnspentPattern} */ @@ -4293,7 +4352,7 @@ function createSpendingSpentUnspentPattern(client, acc) { /** * Create a _6bBlockTxPattern pattern node * @template T - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_6bBlockTxPattern} */ @@ -4313,7 +4372,7 @@ function create_6bBlockTxPattern(client, acc) { /** * Create a AbsoluteRatePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AbsoluteRatePattern} */ @@ -4332,7 +4391,7 @@ function createAbsoluteRatePattern(client, acc) { /** * Create a AbsoluteRatePattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AbsoluteRatePattern2} */ @@ -4351,7 +4410,7 @@ function createAbsoluteRatePattern2(client, acc) { /** * Create a AbsoluteRatePattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AbsoluteRatePattern3} */ @@ -4370,7 +4429,7 @@ function createAbsoluteRatePattern3(client, acc) { /** * Create a AddrUtxoPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AddrUtxoPattern} */ @@ -4389,7 +4448,7 @@ function createAddrUtxoPattern(client, acc) { /** * Create a AllSthPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {AllSthPattern2} */ @@ -4408,7 +4467,7 @@ function createAllSthPattern2(client, acc) { /** * Create a AllSthPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @param {string} disc - Discriminator suffix * @returns {AllSthPattern} @@ -4428,7 +4487,7 @@ function createAllSthPattern(client, acc, disc) { /** * Create a BaseSumPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BaseSumPattern} */ @@ -4447,7 +4506,7 @@ function createBaseSumPattern(client, acc) { /** * Create a BaseDeltaPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BaseDeltaPattern} */ @@ -4466,7 +4525,7 @@ function createBaseDeltaPattern(client, acc) { /** * Create a BlockCumulativePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BlockCumulativePattern} */ @@ -4485,7 +4544,7 @@ function createBlockCumulativePattern(client, acc) { /** * Create a BlocksDominancePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BlocksDominancePattern} */ @@ -4504,7 +4563,7 @@ function createBlocksDominancePattern(client, acc) { /** * Create a BpsRatioPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsRatioPattern2} */ @@ -4523,7 +4582,7 @@ function createBpsRatioPattern2(client, acc) { /** * Create a BpsRatioPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BpsRatioPattern} */ @@ -4542,7 +4601,7 @@ function createBpsRatioPattern(client, acc) { /** * Create a BtcSatsPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {BtcSatsPattern} */ @@ -4561,7 +4620,7 @@ function createBtcSatsPattern(client, acc) { /** * Create a CentsUsdPattern3 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsUsdPattern3} */ @@ -4580,7 +4639,7 @@ function createCentsUsdPattern3(client, acc) { /** * Create a CentsUsdPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsUsdPattern2} */ @@ -4599,7 +4658,7 @@ function createCentsUsdPattern2(client, acc) { /** * Create a CentsUsdPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsUsdPattern} */ @@ -4618,7 +4677,7 @@ function createCentsUsdPattern(client, acc) { /** * Create a CentsUsdPattern4 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CentsUsdPattern4} */ @@ -4637,7 +4696,7 @@ function createCentsUsdPattern4(client, acc) { /** * Create a CoindaysTransferPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {CoindaysTransferPattern} */ @@ -4656,7 +4715,7 @@ function createCoindaysTransferPattern(client, acc) { /** * Create a FundedTotalPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {FundedTotalPattern} */ @@ -4675,7 +4734,7 @@ function createFundedTotalPattern(client, acc) { /** * Create a InPattern2 pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {InPattern2} */ @@ -4694,7 +4753,7 @@ function createInPattern2(client, acc) { /** * Create a InPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {InPattern} */ @@ -4713,7 +4772,7 @@ function createInPattern(client, acc) { /** * Create a PerPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {PerPattern} */ @@ -4732,7 +4791,7 @@ function createPerPattern(client, acc) { /** * Create a PriceRatioPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @param {string} disc - Discriminator suffix * @returns {PriceRatioPattern} @@ -4752,7 +4811,7 @@ function createPriceRatioPattern(client, acc, disc) { /** * Create a RatioValuePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {RatioValuePattern} */ @@ -4777,7 +4836,7 @@ function createRatioValuePattern(client, acc) { /** * Create a ToPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {ToPattern} */ @@ -4795,7 +4854,7 @@ function createToPattern(client, acc) { /** * Create a _24hPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {_24hPattern} */ @@ -4812,7 +4871,7 @@ function create_24hPattern(client, acc) { /** * Create a NuplPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {NuplPattern} */ @@ -4829,7 +4888,7 @@ function createNuplPattern(client, acc) { /** * Create a PricePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {PricePattern} */ @@ -4846,7 +4905,7 @@ function createPricePattern(client, acc) { /** * Create a SharePattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {SharePattern} */ @@ -4863,7 +4922,7 @@ function createSharePattern(client, acc) { /** * Create a TransferPattern pattern node - * @param {BrkClientBase} client + * @param {BrkClient} client * @param {string} acc - Accumulated series name * @returns {TransferPattern} */ @@ -10252,12 +10311,12 @@ class BrkClient extends BrkClientBase { * Compact OpenAPI specification optimized for LLM consumption. Removes redundant fields while preserving essential API information. Full spec available at `/openapi.json`. * * Endpoint: `GET /api.json` - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getApi({ signal, onUpdate } = {}) { + async getApi({ signal, onValue } = {}) { const path = `/api.json`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10270,12 +10329,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/address/{address}` * * @param {Addr} address - * @param {{ signal?: AbortSignal, onUpdate?: (value: AddrStats) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: AddrStats) => void }} [options] * @returns {Promise} */ - async getAddress(address, { signal, onUpdate } = {}) { + async getAddress(address, { signal, onValue } = {}) { const path = `/api/address/${address}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10289,15 +10348,15 @@ class BrkClient extends BrkClientBase { * * @param {Addr} address * @param {Txid=} [after_txid] - Txid to paginate from (return transactions before this one) - * @param {{ signal?: AbortSignal, onUpdate?: (value: Transaction[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Transaction[]) => void }} [options] * @returns {Promise} */ - async getAddressTxs(address, after_txid, { signal, onUpdate } = {}) { + async getAddressTxs(address, after_txid, { signal, onValue } = {}) { const params = new URLSearchParams(); if (after_txid !== undefined) params.set('after_txid', String(after_txid)); const query = params.toString(); const path = `/api/address/${address}/txs${query ? '?' + query : ''}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10311,15 +10370,15 @@ class BrkClient extends BrkClientBase { * * @param {Addr} address * @param {Txid=} [after_txid] - Txid to paginate from (return transactions before this one) - * @param {{ signal?: AbortSignal, onUpdate?: (value: Transaction[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Transaction[]) => void }} [options] * @returns {Promise} */ - async getAddressConfirmedTxs(address, after_txid, { signal, onUpdate } = {}) { + async getAddressConfirmedTxs(address, after_txid, { signal, onValue } = {}) { const params = new URLSearchParams(); if (after_txid !== undefined) params.set('after_txid', String(after_txid)); const query = params.toString(); const path = `/api/address/${address}/txs/chain${query ? '?' + query : ''}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10332,12 +10391,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/address/{address}/txs/mempool` * * @param {Addr} address - * @param {{ signal?: AbortSignal, onUpdate?: (value: Txid[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Txid[]) => void }} [options] * @returns {Promise} */ - async getAddressMempoolTxs(address, { signal, onUpdate } = {}) { + async getAddressMempoolTxs(address, { signal, onValue } = {}) { const path = `/api/address/${address}/txs/mempool`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10350,12 +10409,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/address/{address}/utxo` * * @param {Addr} address - * @param {{ signal?: AbortSignal, onUpdate?: (value: Utxo[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Utxo[]) => void }} [options] * @returns {Promise} */ - async getAddressUtxos(address, { signal, onUpdate } = {}) { + async getAddressUtxos(address, { signal, onValue } = {}) { const path = `/api/address/${address}/utxo`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10368,12 +10427,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block-height/{height}` * * @param {Height} height - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getBlockByHeight(height, { signal, onUpdate } = {}) { + async getBlockByHeight(height, { signal, onValue } = {}) { const path = `/api/block-height/${height}`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10386,12 +10445,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block/{hash}` * * @param {BlockHash} hash - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfo) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfo) => void }} [options] * @returns {Promise} */ - async getBlock(hash, { signal, onUpdate } = {}) { + async getBlock(hash, { signal, onValue } = {}) { const path = `/api/block/${hash}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10404,12 +10463,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block/{hash}/header` * * @param {BlockHash} hash - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getBlockHeader(hash, { signal, onUpdate } = {}) { + async getBlockHeader(hash, { signal, onValue } = {}) { const path = `/api/block/${hash}/header`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10422,12 +10481,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block/{hash}/raw` * * @param {BlockHash} hash - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getBlockRaw(hash, { signal, onUpdate } = {}) { + async getBlockRaw(hash, { signal, onValue } = {}) { const path = `/api/block/${hash}/raw`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10440,12 +10499,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block/{hash}/status` * * @param {BlockHash} hash - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockStatus) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockStatus) => void }} [options] * @returns {Promise} */ - async getBlockStatus(hash, { signal, onUpdate } = {}) { + async getBlockStatus(hash, { signal, onValue } = {}) { const path = `/api/block/${hash}/status`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10459,12 +10518,12 @@ class BrkClient extends BrkClientBase { * * @param {BlockHash} hash - Bitcoin block hash * @param {TxIndex} index - Transaction index within the block (0-based) - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getBlockTxid(hash, index, { signal, onUpdate } = {}) { + async getBlockTxid(hash, index, { signal, onValue } = {}) { const path = `/api/block/${hash}/txid/${index}`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10477,12 +10536,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block/{hash}/txids` * * @param {BlockHash} hash - * @param {{ signal?: AbortSignal, onUpdate?: (value: Txid[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Txid[]) => void }} [options] * @returns {Promise} */ - async getBlockTxids(hash, { signal, onUpdate } = {}) { + async getBlockTxids(hash, { signal, onValue } = {}) { const path = `/api/block/${hash}/txids`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10495,12 +10554,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/block/{hash}/txs` * * @param {BlockHash} hash - * @param {{ signal?: AbortSignal, onUpdate?: (value: Transaction[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Transaction[]) => void }} [options] * @returns {Promise} */ - async getBlockTxs(hash, { signal, onUpdate } = {}) { + async getBlockTxs(hash, { signal, onValue } = {}) { const path = `/api/block/${hash}/txs`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10514,12 +10573,12 @@ class BrkClient extends BrkClientBase { * * @param {BlockHash} hash - Bitcoin block hash * @param {TxIndex} start_index - Starting transaction index within the block (0-based) - * @param {{ signal?: AbortSignal, onUpdate?: (value: Transaction[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Transaction[]) => void }} [options] * @returns {Promise} */ - async getBlockTxsFromIndex(hash, start_index, { signal, onUpdate } = {}) { + async getBlockTxsFromIndex(hash, start_index, { signal, onValue } = {}) { const path = `/api/block/${hash}/txs/${start_index}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10530,12 +10589,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-blocks)* * * Endpoint: `GET /api/blocks` - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfo[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfo[]) => void }} [options] * @returns {Promise} */ - async getBlocks({ signal, onUpdate } = {}) { + async getBlocks({ signal, onValue } = {}) { const path = `/api/blocks`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10546,12 +10605,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-hash)* * * Endpoint: `GET /api/blocks/tip/hash` - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getBlockTipHash({ signal, onUpdate } = {}) { + async getBlockTipHash({ signal, onValue } = {}) { const path = `/api/blocks/tip/hash`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10562,12 +10621,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-block-tip-height)* * * Endpoint: `GET /api/blocks/tip/height` - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getBlockTipHeight({ signal, onUpdate } = {}) { + async getBlockTipHeight({ signal, onValue } = {}) { const path = `/api/blocks/tip/height`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10580,12 +10639,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/blocks/{height}` * * @param {Height} height - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfo[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfo[]) => void }} [options] * @returns {Promise} */ - async getBlocksFromHeight(height, { signal, onUpdate } = {}) { + async getBlocksFromHeight(height, { signal, onValue } = {}) { const path = `/api/blocks/${height}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10596,12 +10655,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool)* * * Endpoint: `GET /api/mempool` - * @param {{ signal?: AbortSignal, onUpdate?: (value: MempoolInfo) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: MempoolInfo) => void }} [options] * @returns {Promise} */ - async getMempool({ signal, onUpdate } = {}) { + async getMempool({ signal, onValue } = {}) { const path = `/api/mempool`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10610,12 +10669,12 @@ class BrkClient extends BrkClientBase { * Returns the current BTC/USD price in dollars, derived from on-chain round-dollar output patterns in the last 12 blocks plus mempool. * * Endpoint: `GET /api/mempool/price` - * @param {{ signal?: AbortSignal, onUpdate?: (value: Dollars) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Dollars) => void }} [options] * @returns {Promise} */ - async getLivePrice({ signal, onUpdate } = {}) { + async getLivePrice({ signal, onValue } = {}) { const path = `/api/mempool/price`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10626,12 +10685,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-recent)* * * Endpoint: `GET /api/mempool/recent` - * @param {{ signal?: AbortSignal, onUpdate?: (value: MempoolRecentTx[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: MempoolRecentTx[]) => void }} [options] * @returns {Promise} */ - async getMempoolRecent({ signal, onUpdate } = {}) { + async getMempoolRecent({ signal, onValue } = {}) { const path = `/api/mempool/recent`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10642,12 +10701,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-transaction-ids)* * * Endpoint: `GET /api/mempool/txids` - * @param {{ signal?: AbortSignal, onUpdate?: (value: Txid[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Txid[]) => void }} [options] * @returns {Promise} */ - async getMempoolTxids({ signal, onUpdate } = {}) { + async getMempoolTxids({ signal, onValue } = {}) { const path = `/api/mempool/txids`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10656,12 +10715,12 @@ class BrkClient extends BrkClientBase { * Returns the complete hierarchical catalog of available series organized as a tree structure. Series are grouped by categories and subcategories. * * Endpoint: `GET /api/series` - * @param {{ signal?: AbortSignal, onUpdate?: (value: TreeNode) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: TreeNode) => void }} [options] * @returns {Promise} */ - async getSeriesTree({ signal, onUpdate } = {}) { + async getSeriesTree({ signal, onValue } = {}) { const path = `/api/series`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10677,10 +10736,10 @@ class BrkClient extends BrkClientBase { * @param {RangeIndex=} [end] - Exclusive end: integer index, date (YYYY-MM-DD), or timestamp (ISO 8601). Negative integers count from end. Aliases: `to`, `t`, `e` * @param {Limit=} [limit] - Maximum number of values to return (ignored if `end` is set). Aliases: `count`, `c`, `l` * @param {Format=} [format] - Format of the output - * @param {{ signal?: AbortSignal, onUpdate?: (value: AnySeriesData[] | string) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: AnySeriesData[] | string) => void }} [options] * @returns {Promise} */ - async getSeriesBulk(series, index, start, end, limit, format, { signal, onUpdate } = {}) { + async getSeriesBulk(series, index, start, end, limit, format, { signal, onValue } = {}) { const params = new URLSearchParams(); params.set('series', String(series)); params.set('index', String(index)); @@ -10690,8 +10749,8 @@ class BrkClient extends BrkClientBase { if (format !== undefined) params.set('format', String(format)); const query = params.toString(); const path = `/api/series/bulk${query ? '?' + query : ''}`; - if (format === 'csv') return this.getText(path, { signal, onUpdate }); - return this.getJson(path, { signal, onUpdate }); + if (format === 'csv') return this.getText(path, { signal, onValue }); + return this.getJson(path, { signal, onValue }); } /** @@ -10700,12 +10759,12 @@ class BrkClient extends BrkClientBase { * Returns the number of series available per index type. * * Endpoint: `GET /api/series/count` - * @param {{ signal?: AbortSignal, onUpdate?: (value: SeriesCount[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: SeriesCount[]) => void }} [options] * @returns {Promise} */ - async getSeriesCount({ signal, onUpdate } = {}) { + async getSeriesCount({ signal, onValue } = {}) { const path = `/api/series/count`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10714,12 +10773,12 @@ class BrkClient extends BrkClientBase { * Returns all available indexes with their accepted query aliases. Use any alias when querying series. * * Endpoint: `GET /api/series/indexes` - * @param {{ signal?: AbortSignal, onUpdate?: (value: IndexInfo[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: IndexInfo[]) => void }} [options] * @returns {Promise} */ - async getIndexes({ signal, onUpdate } = {}) { + async getIndexes({ signal, onValue } = {}) { const path = `/api/series/indexes`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10731,16 +10790,16 @@ class BrkClient extends BrkClientBase { * * @param {number=} [page] - Pagination index * @param {number=} [per_page] - Results per page (default: 1000, max: 1000) - * @param {{ signal?: AbortSignal, onUpdate?: (value: PaginatedSeries) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: PaginatedSeries) => void }} [options] * @returns {Promise} */ - async listSeries(page, per_page, { signal, onUpdate } = {}) { + async listSeries(page, per_page, { signal, onValue } = {}) { const params = new URLSearchParams(); if (page !== undefined) params.set('page', String(page)); if (per_page !== undefined) params.set('per_page', String(per_page)); const query = params.toString(); const path = `/api/series/list${query ? '?' + query : ''}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10752,16 +10811,16 @@ class BrkClient extends BrkClientBase { * * @param {SeriesName} [q] - Search query string * @param {Limit=} [limit] - Maximum number of results - * @param {{ signal?: AbortSignal, onUpdate?: (value: string[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: string[]) => void }} [options] * @returns {Promise} */ - async searchSeries(q, limit, { signal, onUpdate } = {}) { + async searchSeries(q, limit, { signal, onValue } = {}) { const params = new URLSearchParams(); params.set('q', String(q)); if (limit !== undefined) params.set('limit', String(limit)); const query = params.toString(); const path = `/api/series/search${query ? '?' + query : ''}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10772,12 +10831,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/series/{series}` * * @param {SeriesName} series - * @param {{ signal?: AbortSignal, onUpdate?: (value: SeriesInfo) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: SeriesInfo) => void }} [options] * @returns {Promise} */ - async getSeriesInfo(series, { signal, onUpdate } = {}) { + async getSeriesInfo(series, { signal, onValue } = {}) { const path = `/api/series/${series}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10793,10 +10852,10 @@ class BrkClient extends BrkClientBase { * @param {RangeIndex=} [end] - Exclusive end: integer index, date (YYYY-MM-DD), or timestamp (ISO 8601). Negative integers count from end. Aliases: `to`, `t`, `e` * @param {Limit=} [limit] - Maximum number of values to return (ignored if `end` is set). Aliases: `count`, `c`, `l` * @param {Format=} [format] - Format of the output - * @param {{ signal?: AbortSignal, onUpdate?: (value: AnySeriesData | string) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: AnySeriesData | string) => void }} [options] * @returns {Promise} */ - async getSeries(series, index, start, end, limit, format, { signal, onUpdate } = {}) { + async getSeries(series, index, start, end, limit, format, { signal, onValue } = {}) { const params = new URLSearchParams(); if (start !== undefined) params.set('start', String(start)); if (end !== undefined) params.set('end', String(end)); @@ -10804,8 +10863,8 @@ class BrkClient extends BrkClientBase { if (format !== undefined) params.set('format', String(format)); const query = params.toString(); const path = `/api/series/${series}/${index}${query ? '?' + query : ''}`; - if (format === 'csv') return this.getText(path, { signal, onUpdate }); - return this.getJson(path, { signal, onUpdate }); + if (format === 'csv') return this.getText(path, { signal, onValue }); + return this.getJson(path, { signal, onValue }); } /** @@ -10821,10 +10880,10 @@ class BrkClient extends BrkClientBase { * @param {RangeIndex=} [end] - Exclusive end: integer index, date (YYYY-MM-DD), or timestamp (ISO 8601). Negative integers count from end. Aliases: `to`, `t`, `e` * @param {Limit=} [limit] - Maximum number of values to return (ignored if `end` is set). Aliases: `count`, `c`, `l` * @param {Format=} [format] - Format of the output - * @param {{ signal?: AbortSignal, onUpdate?: (value: boolean[] | string) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: boolean[] | string) => void }} [options] * @returns {Promise} */ - async getSeriesData(series, index, start, end, limit, format, { signal, onUpdate } = {}) { + async getSeriesData(series, index, start, end, limit, format, { signal, onValue } = {}) { const params = new URLSearchParams(); if (start !== undefined) params.set('start', String(start)); if (end !== undefined) params.set('end', String(end)); @@ -10832,8 +10891,8 @@ class BrkClient extends BrkClientBase { if (format !== undefined) params.set('format', String(format)); const query = params.toString(); const path = `/api/series/${series}/${index}/data${query ? '?' + query : ''}`; - if (format === 'csv') return this.getText(path, { signal, onUpdate }); - return this.getJson(path, { signal, onUpdate }); + if (format === 'csv') return this.getText(path, { signal, onValue }); + return this.getJson(path, { signal, onValue }); } /** @@ -10845,12 +10904,12 @@ class BrkClient extends BrkClientBase { * * @param {SeriesName} series - Series name * @param {Index} index - Aggregation index - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getSeriesLatest(series, index, { signal, onUpdate } = {}) { + async getSeriesLatest(series, index, { signal, onValue } = {}) { const path = `/api/series/${series}/${index}/latest`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10862,12 +10921,12 @@ class BrkClient extends BrkClientBase { * * @param {SeriesName} series - Series name * @param {Index} index - Aggregation index - * @param {{ signal?: AbortSignal, onUpdate?: (value: number) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: number) => void }} [options] * @returns {Promise} */ - async getSeriesLen(series, index, { signal, onUpdate } = {}) { + async getSeriesLen(series, index, { signal, onValue } = {}) { const path = `/api/series/${series}/${index}/len`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10879,12 +10938,12 @@ class BrkClient extends BrkClientBase { * * @param {SeriesName} series - Series name * @param {Index} index - Aggregation index - * @param {{ signal?: AbortSignal, onUpdate?: (value: Version) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Version) => void }} [options] * @returns {Promise} */ - async getSeriesVersion(series, index, { signal, onUpdate } = {}) { + async getSeriesVersion(series, index, { signal, onValue } = {}) { const path = `/api/series/${series}/${index}/version`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10893,12 +10952,12 @@ class BrkClient extends BrkClientBase { * Returns the disk space used by BRK and Bitcoin data. * * Endpoint: `GET /api/server/disk` - * @param {{ signal?: AbortSignal, onUpdate?: (value: DiskUsage) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: DiskUsage) => void }} [options] * @returns {Promise} */ - async getDiskUsage({ signal, onUpdate } = {}) { + async getDiskUsage({ signal, onValue } = {}) { const path = `/api/server/disk`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10907,12 +10966,12 @@ class BrkClient extends BrkClientBase { * Returns the sync status of the indexer, including indexed height, tip height, blocks behind, and last indexed timestamp. * * Endpoint: `GET /api/server/sync` - * @param {{ signal?: AbortSignal, onUpdate?: (value: SyncStatus) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: SyncStatus) => void }} [options] * @returns {Promise} */ - async getSyncStatus({ signal, onUpdate } = {}) { + async getSyncStatus({ signal, onValue } = {}) { const path = `/api/server/sync`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10923,12 +10982,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx-index/{index}` * * @param {TxIndex} index - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getTxByIndex(index, { signal, onUpdate } = {}) { + async getTxByIndex(index, { signal, onValue } = {}) { const path = `/api/tx-index/${index}`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10941,12 +11000,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: Transaction) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Transaction) => void }} [options] * @returns {Promise} */ - async getTx(txid, { signal, onUpdate } = {}) { + async getTx(txid, { signal, onValue } = {}) { const path = `/api/tx/${txid}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10959,12 +11018,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/hex` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getTxHex(txid, { signal, onUpdate } = {}) { + async getTxHex(txid, { signal, onValue } = {}) { const path = `/api/tx/${txid}/hex`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -10977,12 +11036,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/merkle-proof` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: MerkleProof) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: MerkleProof) => void }} [options] * @returns {Promise} */ - async getTxMerkleProof(txid, { signal, onUpdate } = {}) { + async getTxMerkleProof(txid, { signal, onValue } = {}) { const path = `/api/tx/${txid}/merkle-proof`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -10995,12 +11054,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/merkleblock-proof` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getTxMerkleblockProof(txid, { signal, onUpdate } = {}) { + async getTxMerkleblockProof(txid, { signal, onValue } = {}) { const path = `/api/tx/${txid}/merkleblock-proof`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -11014,12 +11073,12 @@ class BrkClient extends BrkClientBase { * * @param {Txid} txid - Transaction ID * @param {Vout} vout - Output index - * @param {{ signal?: AbortSignal, onUpdate?: (value: TxOutspend) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: TxOutspend) => void }} [options] * @returns {Promise} */ - async getTxOutspend(txid, vout, { signal, onUpdate } = {}) { + async getTxOutspend(txid, vout, { signal, onValue } = {}) { const path = `/api/tx/${txid}/outspend/${vout}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11032,12 +11091,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/outspends` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: TxOutspend[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: TxOutspend[]) => void }} [options] * @returns {Promise} */ - async getTxOutspends(txid, { signal, onUpdate } = {}) { + async getTxOutspends(txid, { signal, onValue } = {}) { const path = `/api/tx/${txid}/outspends`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11050,12 +11109,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/raw` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getTxRaw(txid, { signal, onUpdate } = {}) { + async getTxRaw(txid, { signal, onValue } = {}) { const path = `/api/tx/${txid}/raw`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -11068,12 +11127,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/tx/{txid}/status` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: TxStatus) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: TxStatus) => void }} [options] * @returns {Promise} */ - async getTxStatus(txid, { signal, onUpdate } = {}) { + async getTxStatus(txid, { signal, onValue } = {}) { const path = `/api/tx/${txid}/status`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11082,12 +11141,12 @@ class BrkClient extends BrkClientBase { * Cohorts for which URPD data is available. Returns names like `all`, `sth`, `lth`, `utxos_under_1h_old`. * * Endpoint: `GET /api/urpd` - * @param {{ signal?: AbortSignal, onUpdate?: (value: Cohort[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Cohort[]) => void }} [options] * @returns {Promise} */ - async listUrpdCohorts({ signal, onUpdate } = {}) { + async listUrpdCohorts({ signal, onValue } = {}) { const path = `/api/urpd`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11101,15 +11160,15 @@ class BrkClient extends BrkClientBase { * * @param {Cohort} cohort * @param {UrpdAggregation=} [agg] - Aggregation strategy. Default: raw (no aggregation). Accepts `bucket` as alias. - * @param {{ signal?: AbortSignal, onUpdate?: (value: Urpd) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Urpd) => void }} [options] * @returns {Promise} */ - async getUrpd(cohort, agg, { signal, onUpdate } = {}) { + async getUrpd(cohort, agg, { signal, onValue } = {}) { const params = new URLSearchParams(); if (agg !== undefined) params.set('agg', String(agg)); const query = params.toString(); const path = `/api/urpd/${cohort}${query ? '?' + query : ''}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11120,12 +11179,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/urpd/{cohort}/dates` * * @param {Cohort} cohort - * @param {{ signal?: AbortSignal, onUpdate?: (value: Date[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Date[]) => void }} [options] * @returns {Promise} */ - async listUrpdDates(cohort, { signal, onUpdate } = {}) { + async listUrpdDates(cohort, { signal, onValue } = {}) { const path = `/api/urpd/${cohort}/dates`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11140,15 +11199,15 @@ class BrkClient extends BrkClientBase { * @param {Cohort} cohort * @param {string} date * @param {UrpdAggregation=} [agg] - Aggregation strategy. Default: raw (no aggregation). Accepts `bucket` as alias. - * @param {{ signal?: AbortSignal, onUpdate?: (value: Urpd) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Urpd) => void }} [options] * @returns {Promise} */ - async getUrpdAt(cohort, date, agg, { signal, onUpdate } = {}) { + async getUrpdAt(cohort, date, agg, { signal, onValue } = {}) { const params = new URLSearchParams(); if (agg !== undefined) params.set('agg', String(agg)); const query = params.toString(); const path = `/api/urpd/${cohort}/${date}${query ? '?' + query : ''}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11161,12 +11220,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/block/{hash}` * * @param {BlockHash} hash - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfoV1) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfoV1) => void }} [options] * @returns {Promise} */ - async getBlockV1(hash, { signal, onUpdate } = {}) { + async getBlockV1(hash, { signal, onValue } = {}) { const path = `/api/v1/block/${hash}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11177,12 +11236,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-blocks-v1)* * * Endpoint: `GET /api/v1/blocks` - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfoV1[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfoV1[]) => void }} [options] * @returns {Promise} */ - async getBlocksV1({ signal, onUpdate } = {}) { + async getBlocksV1({ signal, onValue } = {}) { const path = `/api/v1/blocks`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11195,12 +11254,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/blocks/{height}` * * @param {Height} height - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfoV1[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfoV1[]) => void }} [options] * @returns {Promise} */ - async getBlocksV1FromHeight(height, { signal, onUpdate } = {}) { + async getBlocksV1FromHeight(height, { signal, onValue } = {}) { const path = `/api/v1/blocks/${height}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11213,12 +11272,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/cpfp/{txid}` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: CpfpInfo) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: CpfpInfo) => void }} [options] * @returns {Promise} */ - async getCpfp(txid, { signal, onUpdate } = {}) { + async getCpfp(txid, { signal, onValue } = {}) { const path = `/api/v1/cpfp/${txid}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11229,12 +11288,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustment)* * * Endpoint: `GET /api/v1/difficulty-adjustment` - * @param {{ signal?: AbortSignal, onUpdate?: (value: DifficultyAdjustment) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: DifficultyAdjustment) => void }} [options] * @returns {Promise} */ - async getDifficultyAdjustment({ signal, onUpdate } = {}) { + async getDifficultyAdjustment({ signal, onValue } = {}) { const path = `/api/v1/difficulty-adjustment`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11245,12 +11304,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mempool-blocks-fees)* * * Endpoint: `GET /api/v1/fees/mempool-blocks` - * @param {{ signal?: AbortSignal, onUpdate?: (value: MempoolBlock[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: MempoolBlock[]) => void }} [options] * @returns {Promise} */ - async getMempoolBlocks({ signal, onUpdate } = {}) { + async getMempoolBlocks({ signal, onValue } = {}) { const path = `/api/v1/fees/mempool-blocks`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11261,12 +11320,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees-precise)* * * Endpoint: `GET /api/v1/fees/precise` - * @param {{ signal?: AbortSignal, onUpdate?: (value: RecommendedFees) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: RecommendedFees) => void }} [options] * @returns {Promise} */ - async getPreciseFees({ signal, onUpdate } = {}) { + async getPreciseFees({ signal, onValue } = {}) { const path = `/api/v1/fees/precise`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11277,12 +11336,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-recommended-fees)* * * Endpoint: `GET /api/v1/fees/recommended` - * @param {{ signal?: AbortSignal, onUpdate?: (value: RecommendedFees) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: RecommendedFees) => void }} [options] * @returns {Promise} */ - async getRecommendedFees({ signal, onUpdate } = {}) { + async getRecommendedFees({ signal, onValue } = {}) { const path = `/api/v1/fees/recommended`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11295,15 +11354,15 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/historical-price` * * @param {Timestamp=} [timestamp] - * @param {{ signal?: AbortSignal, onUpdate?: (value: HistoricalPrice) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: HistoricalPrice) => void }} [options] * @returns {Promise} */ - async getHistoricalPrice(timestamp, { signal, onUpdate } = {}) { + async getHistoricalPrice(timestamp, { signal, onValue } = {}) { const params = new URLSearchParams(); if (timestamp !== undefined) params.set('timestamp', String(timestamp)); const query = params.toString(); const path = `/api/v1/historical-price${query ? '?' + query : ''}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11316,12 +11375,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/blocks/fee-rates/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockFeeRatesEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockFeeRatesEntry[]) => void }} [options] * @returns {Promise} */ - async getBlockFeeRates(time_period, { signal, onUpdate } = {}) { + async getBlockFeeRates(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/blocks/fee-rates/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11334,12 +11393,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/blocks/fees/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockFeesEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockFeesEntry[]) => void }} [options] * @returns {Promise} */ - async getBlockFees(time_period, { signal, onUpdate } = {}) { + async getBlockFees(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/blocks/fees/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11352,12 +11411,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/blocks/rewards/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockRewardsEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockRewardsEntry[]) => void }} [options] * @returns {Promise} */ - async getBlockRewards(time_period, { signal, onUpdate } = {}) { + async getBlockRewards(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/blocks/rewards/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11370,12 +11429,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/blocks/sizes-weights/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockSizesWeights) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockSizesWeights) => void }} [options] * @returns {Promise} */ - async getBlockSizesWeights(time_period, { signal, onUpdate } = {}) { + async getBlockSizesWeights(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/blocks/sizes-weights/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11388,12 +11447,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/blocks/timestamp/{timestamp}` * * @param {Timestamp} timestamp - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockTimestamp) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockTimestamp) => void }} [options] * @returns {Promise} */ - async getBlockByTimestamp(timestamp, { signal, onUpdate } = {}) { + async getBlockByTimestamp(timestamp, { signal, onValue } = {}) { const path = `/api/v1/mining/blocks/timestamp/${timestamp}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11404,12 +11463,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-difficulty-adjustments)* * * Endpoint: `GET /api/v1/mining/difficulty-adjustments` - * @param {{ signal?: AbortSignal, onUpdate?: (value: DifficultyAdjustmentEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: DifficultyAdjustmentEntry[]) => void }} [options] * @returns {Promise} */ - async getDifficultyAdjustments({ signal, onUpdate } = {}) { + async getDifficultyAdjustments({ signal, onValue } = {}) { const path = `/api/v1/mining/difficulty-adjustments`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11422,12 +11481,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/difficulty-adjustments/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: DifficultyAdjustmentEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: DifficultyAdjustmentEntry[]) => void }} [options] * @returns {Promise} */ - async getDifficultyAdjustmentsByPeriod(time_period, { signal, onUpdate } = {}) { + async getDifficultyAdjustmentsByPeriod(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/difficulty-adjustments/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11438,12 +11497,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-hashrate)* * * Endpoint: `GET /api/v1/mining/hashrate` - * @param {{ signal?: AbortSignal, onUpdate?: (value: HashrateSummary) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: HashrateSummary) => void }} [options] * @returns {Promise} */ - async getHashrate({ signal, onUpdate } = {}) { + async getHashrate({ signal, onValue } = {}) { const path = `/api/v1/mining/hashrate`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11454,12 +11513,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pool-hashrates)* * * Endpoint: `GET /api/v1/mining/hashrate/pools` - * @param {{ signal?: AbortSignal, onUpdate?: (value: PoolHashrateEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: PoolHashrateEntry[]) => void }} [options] * @returns {Promise} */ - async getPoolsHashrate({ signal, onUpdate } = {}) { + async getPoolsHashrate({ signal, onValue } = {}) { const path = `/api/v1/mining/hashrate/pools`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11472,12 +11531,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/hashrate/pools/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: PoolHashrateEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: PoolHashrateEntry[]) => void }} [options] * @returns {Promise} */ - async getPoolsHashrateByPeriod(time_period, { signal, onUpdate } = {}) { + async getPoolsHashrateByPeriod(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/hashrate/pools/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11490,12 +11549,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/hashrate/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: HashrateSummary) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: HashrateSummary) => void }} [options] * @returns {Promise} */ - async getHashrateByPeriod(time_period, { signal, onUpdate } = {}) { + async getHashrateByPeriod(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/hashrate/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11508,12 +11567,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/pool/{slug}` * * @param {PoolSlug} slug - * @param {{ signal?: AbortSignal, onUpdate?: (value: PoolDetail) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: PoolDetail) => void }} [options] * @returns {Promise} */ - async getPool(slug, { signal, onUpdate } = {}) { + async getPool(slug, { signal, onValue } = {}) { const path = `/api/v1/mining/pool/${slug}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11526,12 +11585,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/pool/{slug}/blocks` * * @param {PoolSlug} slug - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfoV1[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfoV1[]) => void }} [options] * @returns {Promise} */ - async getPoolBlocks(slug, { signal, onUpdate } = {}) { + async getPoolBlocks(slug, { signal, onValue } = {}) { const path = `/api/v1/mining/pool/${slug}/blocks`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11545,12 +11604,12 @@ class BrkClient extends BrkClientBase { * * @param {PoolSlug} slug * @param {Height} height - * @param {{ signal?: AbortSignal, onUpdate?: (value: BlockInfoV1[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: BlockInfoV1[]) => void }} [options] * @returns {Promise} */ - async getPoolBlocksFrom(slug, height, { signal, onUpdate } = {}) { + async getPoolBlocksFrom(slug, height, { signal, onValue } = {}) { const path = `/api/v1/mining/pool/${slug}/blocks/${height}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11563,12 +11622,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/pool/{slug}/hashrate` * * @param {PoolSlug} slug - * @param {{ signal?: AbortSignal, onUpdate?: (value: PoolHashrateEntry[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: PoolHashrateEntry[]) => void }} [options] * @returns {Promise} */ - async getPoolHashrate(slug, { signal, onUpdate } = {}) { + async getPoolHashrate(slug, { signal, onValue } = {}) { const path = `/api/v1/mining/pool/${slug}/hashrate`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11579,12 +11638,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-mining-pools)* * * Endpoint: `GET /api/v1/mining/pools` - * @param {{ signal?: AbortSignal, onUpdate?: (value: PoolInfo[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: PoolInfo[]) => void }} [options] * @returns {Promise} */ - async getPools({ signal, onUpdate } = {}) { + async getPools({ signal, onValue } = {}) { const path = `/api/v1/mining/pools`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11597,12 +11656,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/pools/{time_period}` * * @param {TimePeriod} time_period - * @param {{ signal?: AbortSignal, onUpdate?: (value: PoolsSummary) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: PoolsSummary) => void }} [options] * @returns {Promise} */ - async getPoolStats(time_period, { signal, onUpdate } = {}) { + async getPoolStats(time_period, { signal, onValue } = {}) { const path = `/api/v1/mining/pools/${time_period}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11615,12 +11674,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/mining/reward-stats/{block_count}` * * @param {number} block_count - Number of recent blocks to include - * @param {{ signal?: AbortSignal, onUpdate?: (value: RewardStats) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: RewardStats) => void }} [options] * @returns {Promise} */ - async getRewardStats(block_count, { signal, onUpdate } = {}) { + async getRewardStats(block_count, { signal, onValue } = {}) { const path = `/api/v1/mining/reward-stats/${block_count}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11631,12 +11690,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-price)* * * Endpoint: `GET /api/v1/prices` - * @param {{ signal?: AbortSignal, onUpdate?: (value: Prices) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Prices) => void }} [options] * @returns {Promise} */ - async getPrices({ signal, onUpdate } = {}) { + async getPrices({ signal, onValue } = {}) { const path = `/api/v1/prices`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11647,12 +11706,12 @@ class BrkClient extends BrkClientBase { * *[Mempool.space docs](https://mempool.space/docs/api/rest#get-transaction-times)* * * Endpoint: `GET /api/v1/transaction-times` - * @param {{ signal?: AbortSignal, onUpdate?: (value: number[]) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: number[]) => void }} [options] * @returns {Promise} */ - async getTransactionTimes({ signal, onUpdate } = {}) { + async getTransactionTimes({ signal, onValue } = {}) { const path = `/api/v1/transaction-times`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11665,12 +11724,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/tx/{txid}/rbf` * * @param {Txid} txid - * @param {{ signal?: AbortSignal, onUpdate?: (value: RbfResponse) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: RbfResponse) => void }} [options] * @returns {Promise} */ - async getTxRbf(txid, { signal, onUpdate } = {}) { + async getTxRbf(txid, { signal, onValue } = {}) { const path = `/api/v1/tx/${txid}/rbf`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11683,12 +11742,12 @@ class BrkClient extends BrkClientBase { * Endpoint: `GET /api/v1/validate-address/{address}` * * @param {string} address - Bitcoin address to validate (can be any string) - * @param {{ signal?: AbortSignal, onUpdate?: (value: AddrValidation) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: AddrValidation) => void }} [options] * @returns {Promise} */ - async validateAddress(address, { signal, onUpdate } = {}) { + async validateAddress(address, { signal, onValue } = {}) { const path = `/api/v1/validate-address/${address}`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11697,12 +11756,12 @@ class BrkClient extends BrkClientBase { * Returns the health status of the API server, including uptime information. * * Endpoint: `GET /health` - * @param {{ signal?: AbortSignal, onUpdate?: (value: Health) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: Health) => void }} [options] * @returns {Promise} */ - async getHealth({ signal, onUpdate } = {}) { + async getHealth({ signal, onValue } = {}) { const path = `/health`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } /** @@ -11711,12 +11770,12 @@ class BrkClient extends BrkClientBase { * Full OpenAPI 3.1 specification for this API. * * Endpoint: `GET /openapi.json` - * @param {{ signal?: AbortSignal, onUpdate?: (value: *) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: *) => void }} [options] * @returns {Promise<*>} */ - async getOpenapi({ signal, onUpdate } = {}) { + async getOpenapi({ signal, onValue } = {}) { const path = `/openapi.json`; - return this.getText(path, { signal, onUpdate }); + return this.getText(path, { signal, onValue }); } /** @@ -11725,12 +11784,12 @@ class BrkClient extends BrkClientBase { * Returns the current version of the API server * * Endpoint: `GET /version` - * @param {{ signal?: AbortSignal, onUpdate?: (value: string) => void }} [options] + * @param {{ signal?: AbortSignal, onValue?: (value: string) => void }} [options] * @returns {Promise} */ - async getVersion({ signal, onUpdate } = {}) { + async getVersion({ signal, onValue } = {}) { const path = `/version`; - return this.getJson(path, { signal, onUpdate }); + return this.getJson(path, { signal, onValue }); } } diff --git a/packages/brk_client/brk_client/__init__.py b/packages/brk_client/brk_client/__init__.py index 619dfddd4..2eec30a6e 100644 --- a/packages/brk_client/brk_client/__init__.py +++ b/packages/brk_client/brk_client/__init__.py @@ -1916,13 +1916,13 @@ AnyDateSeriesData = DateSeriesData[Any] class _EndpointConfig: """Shared endpoint configuration.""" - client: BrkClientBase + client: BrkClient name: str index: Index start: Optional[int] end: Optional[int] - def __init__(self, client: BrkClientBase, name: str, index: Index, + def __init__(self, client: BrkClient, name: str, index: Index, start: Optional[int] = None, end: Optional[int] = None): self.client = client self.name = name @@ -1957,6 +1957,12 @@ class _EndpointConfig: def get_csv(self) -> str: return self.client.get_text(self._build_path(format='csv')) + def get_len(self) -> int: + return self.client.get_series_len(self.name, self.index) + + def get_version(self) -> Version: + return self.client.get_series_version(self.name, self.index) + class RangeBuilder(Generic[T]): """Builder with range specified.""" @@ -2040,7 +2046,7 @@ class SeriesEndpoint(Generic[T]): data = endpoint.skip(100).take(10).fetch() """ - def __init__(self, client: BrkClientBase, name: str, index: Index): + def __init__(self, client: BrkClient, name: str, index: Index): self._config = _EndpointConfig(client, name, index) @overload @@ -2074,6 +2080,14 @@ class SeriesEndpoint(Generic[T]): """Fetch all data as CSV.""" return self._config.get_csv() + def len(self) -> int: + """Total number of data points for this series.""" + return self._config.get_len() + + def version(self) -> Version: + """Current version of the series.""" + return self._config.get_version() + def path(self) -> str: """Get the base endpoint path.""" return self._config.path() @@ -2091,7 +2105,7 @@ class DateSeriesEndpoint(Generic[T]): data = endpoint[:10].fetch() """ - def __init__(self, client: BrkClientBase, name: str, index: Index): + def __init__(self, client: BrkClient, name: str, index: Index): self._config = _EndpointConfig(client, name, index) @overload @@ -2137,6 +2151,14 @@ class DateSeriesEndpoint(Generic[T]): """Fetch all data as CSV.""" return self._config.get_csv() + def len(self) -> int: + """Total number of data points for this series.""" + return self._config.get_len() + + def version(self) -> Version: + """Current version of the series.""" + return self._config.get_version() + def path(self) -> str: """Get the base endpoint path.""" return self._config.path() @@ -2201,16 +2223,16 @@ _i33 = ('unknown_output_index',) _i34 = ('funded_addr_index',) _i35 = ('empty_addr_index',) -def _ep(c: BrkClientBase, n: str, i: Index) -> SeriesEndpoint[Any]: +def _ep(c: BrkClient, n: str, i: Index) -> SeriesEndpoint[Any]: return SeriesEndpoint(c, n, i) -def _dep(c: BrkClientBase, n: str, i: Index) -> DateSeriesEndpoint[Any]: +def _dep(c: BrkClient, n: str, i: Index) -> DateSeriesEndpoint[Any]: return DateSeriesEndpoint(c, n, i) # Index accessor classes class _SeriesPattern1By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def minute10(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'minute10') def minute30(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'minute30') def hour1(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'hour1') @@ -2230,14 +2252,14 @@ class _SeriesPattern1By(Generic[T]): class SeriesPattern1(Generic[T]): by: _SeriesPattern1By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern1By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern1By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i1) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i1 else None class _SeriesPattern2By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def minute10(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'minute10') def minute30(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'minute30') def hour1(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'hour1') @@ -2256,403 +2278,403 @@ class _SeriesPattern2By(Generic[T]): class SeriesPattern2(Generic[T]): by: _SeriesPattern2By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern2By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern2By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i2) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i2 else None class _SeriesPattern3By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def minute10(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'minute10') class SeriesPattern3(Generic[T]): by: _SeriesPattern3By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern3By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern3By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i3) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i3 else None class _SeriesPattern4By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def minute30(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'minute30') class SeriesPattern4(Generic[T]): by: _SeriesPattern4By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern4By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern4By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i4) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i4 else None class _SeriesPattern5By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def hour1(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'hour1') class SeriesPattern5(Generic[T]): by: _SeriesPattern5By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern5By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern5By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i5) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i5 else None class _SeriesPattern6By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def hour4(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'hour4') class SeriesPattern6(Generic[T]): by: _SeriesPattern6By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern6By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern6By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i6) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i6 else None class _SeriesPattern7By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def hour12(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'hour12') class SeriesPattern7(Generic[T]): by: _SeriesPattern7By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern7By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern7By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i7) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i7 else None class _SeriesPattern8By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def day1(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'day1') class SeriesPattern8(Generic[T]): by: _SeriesPattern8By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern8By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern8By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i8) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i8 else None class _SeriesPattern9By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def day3(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'day3') class SeriesPattern9(Generic[T]): by: _SeriesPattern9By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern9By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern9By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i9) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i9 else None class _SeriesPattern10By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def week1(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'week1') class SeriesPattern10(Generic[T]): by: _SeriesPattern10By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern10By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern10By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i10) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i10 else None class _SeriesPattern11By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def month1(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'month1') class SeriesPattern11(Generic[T]): by: _SeriesPattern11By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern11By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern11By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i11) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i11 else None class _SeriesPattern12By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def month3(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'month3') class SeriesPattern12(Generic[T]): by: _SeriesPattern12By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern12By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern12By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i12) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i12 else None class _SeriesPattern13By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def month6(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'month6') class SeriesPattern13(Generic[T]): by: _SeriesPattern13By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern13By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern13By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i13) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i13 else None class _SeriesPattern14By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def year1(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'year1') class SeriesPattern14(Generic[T]): by: _SeriesPattern14By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern14By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern14By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i14) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i14 else None class _SeriesPattern15By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def year10(self) -> DateSeriesEndpoint[T]: return _dep(self._c, self._n, 'year10') class SeriesPattern15(Generic[T]): by: _SeriesPattern15By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern15By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern15By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i15) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i15 else None class _SeriesPattern16By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def halving(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'halving') class SeriesPattern16(Generic[T]): by: _SeriesPattern16By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern16By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern16By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i16) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i16 else None class _SeriesPattern17By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def epoch(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'epoch') class SeriesPattern17(Generic[T]): by: _SeriesPattern17By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern17By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern17By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i17) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i17 else None class _SeriesPattern18By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def height(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'height') class SeriesPattern18(Generic[T]): by: _SeriesPattern18By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern18By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern18By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i18) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i18 else None class _SeriesPattern19By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def tx_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'tx_index') class SeriesPattern19(Generic[T]): by: _SeriesPattern19By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern19By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern19By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i19) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i19 else None class _SeriesPattern20By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def txin_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'txin_index') class SeriesPattern20(Generic[T]): by: _SeriesPattern20By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern20By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern20By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i20) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i20 else None class _SeriesPattern21By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def txout_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'txout_index') class SeriesPattern21(Generic[T]): by: _SeriesPattern21By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern21By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern21By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i21) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i21 else None class _SeriesPattern22By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def empty_output_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'empty_output_index') class SeriesPattern22(Generic[T]): by: _SeriesPattern22By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern22By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern22By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i22) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i22 else None class _SeriesPattern23By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def op_return_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'op_return_index') class SeriesPattern23(Generic[T]): by: _SeriesPattern23By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern23By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern23By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i23) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i23 else None class _SeriesPattern24By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2a_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2a_addr_index') class SeriesPattern24(Generic[T]): by: _SeriesPattern24By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern24By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern24By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i24) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i24 else None class _SeriesPattern25By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2ms_output_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2ms_output_index') class SeriesPattern25(Generic[T]): by: _SeriesPattern25By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern25By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern25By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i25) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i25 else None class _SeriesPattern26By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2pk33_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2pk33_addr_index') class SeriesPattern26(Generic[T]): by: _SeriesPattern26By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern26By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern26By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i26) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i26 else None class _SeriesPattern27By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2pk65_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2pk65_addr_index') class SeriesPattern27(Generic[T]): by: _SeriesPattern27By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern27By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern27By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i27) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i27 else None class _SeriesPattern28By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2pkh_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2pkh_addr_index') class SeriesPattern28(Generic[T]): by: _SeriesPattern28By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern28By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern28By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i28) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i28 else None class _SeriesPattern29By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2sh_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2sh_addr_index') class SeriesPattern29(Generic[T]): by: _SeriesPattern29By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern29By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern29By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i29) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i29 else None class _SeriesPattern30By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2tr_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2tr_addr_index') class SeriesPattern30(Generic[T]): by: _SeriesPattern30By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern30By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern30By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i30) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i30 else None class _SeriesPattern31By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2wpkh_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2wpkh_addr_index') class SeriesPattern31(Generic[T]): by: _SeriesPattern31By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern31By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern31By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i31) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i31 else None class _SeriesPattern32By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def p2wsh_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'p2wsh_addr_index') class SeriesPattern32(Generic[T]): by: _SeriesPattern32By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern32By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern32By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i32) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i32 else None class _SeriesPattern33By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def unknown_output_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'unknown_output_index') class SeriesPattern33(Generic[T]): by: _SeriesPattern33By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern33By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern33By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i33) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i33 else None class _SeriesPattern34By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def funded_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'funded_addr_index') class SeriesPattern34(Generic[T]): by: _SeriesPattern34By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern34By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern34By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i34) def get(self, index: Index) -> Optional[SeriesEndpoint[T]]: return _ep(self.by._c, self._n, index) if index in _i34 else None class _SeriesPattern35By(Generic[T]): - def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n + def __init__(self, c: BrkClient, n: str): self._c, self._n = c, n def empty_addr_index(self) -> SeriesEndpoint[T]: return _ep(self._c, self._n, 'empty_addr_index') class SeriesPattern35(Generic[T]): by: _SeriesPattern35By[T] - def __init__(self, c: BrkClientBase, n: str): self._n, self.by = n, _SeriesPattern35By(c, n) + def __init__(self, c: BrkClient, n: str): self._n, self.by = n, _SeriesPattern35By(c, n) @property def name(self) -> str: return self._n def indexes(self) -> List[str]: return list(_i35) @@ -2663,7 +2685,7 @@ class SeriesPattern35(Generic[T]): class Pct05Pct10Pct15Pct20Pct25Pct30Pct35Pct40Pct45Pct50Pct55Pct60Pct65Pct70Pct75Pct80Pct85Pct90Pct95Pattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.pct05: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct05')) self.pct10: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct10')) @@ -2692,7 +2714,7 @@ class _0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern: class AllEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.all: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'bis')) self.empty: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'with_empty_outputs_output')) @@ -2711,7 +2733,7 @@ class AllEmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern: class _10y1m1w1y2y3m3y4y5y6m6y8yPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._10y: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, '10y')) self._1m: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, '1m')) @@ -2729,7 +2751,7 @@ class _10y1m1w1y2y3m3y4y5y6m6y8yPattern2: class _10y1m1w1y2y3m3y4y5y6m6y8yPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._10y: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, '10y')) self._1m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, '1m')) @@ -2755,7 +2777,7 @@ class CapCapitalizedGrossLossMvrvNetPeakPriceProfitSellSoprPattern: class EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.empty: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, _m(acc, 'empty_outputs_output')) self.op_return: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, _m(acc, 'op_return_output')) @@ -2773,7 +2795,7 @@ class EmptyOpP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2: class AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.average: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, _m(acc, 'average')) self.block: SeriesPattern18[StoredU64] = SeriesPattern18(client, acc) @@ -2790,7 +2812,7 @@ class AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern: class EmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.empty: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, _m(acc, 'empty_outputs_prevout')) self.p2a: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, _m(acc, 'p2a_prevout')) @@ -2807,7 +2829,7 @@ class EmptyP2aP2msP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshUnknownPattern2: class AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern(Generic[T]): """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.average: _1m1w1y24hPattern[T] = _1m1w1y24hPattern(client, _m(acc, 'average')) self.base: SeriesPattern18[T] = SeriesPattern18(client, acc) @@ -2828,7 +2850,7 @@ class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshSharePattern: class IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.index: SeriesPattern1[StoredI8] = SeriesPattern1(client, _m(acc, 'index')) self.pct0_5: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'pct0_5')) @@ -2844,7 +2866,7 @@ class IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern: class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.all: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, acc) self.p2a: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, _p('p2a', acc)) @@ -2859,7 +2881,7 @@ class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6: class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.all: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, acc) self.p2a: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _p('p2a', acc)) @@ -2874,7 +2896,7 @@ class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern5: class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.all: SeriesPattern1[StoredU64] = SeriesPattern1(client, acc) self.p2a: SeriesPattern1[StoredU64] = SeriesPattern1(client, _p('p2a', acc)) @@ -2889,7 +2911,7 @@ class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4: class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.all: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, acc) self.p2a: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, _p('p2a', acc)) @@ -2904,7 +2926,7 @@ class AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7: class AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.average: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, _m(acc, 'average')) self.max: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'max')) @@ -2919,7 +2941,7 @@ class AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern: class CapitalizedGrossInvestedLossNetNuplProfitSentimentPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.capitalized_cap_in_loss_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, _m(acc, 'capitalized_cap_in_loss_raw')) self.capitalized_cap_in_profit_raw: SeriesPattern18[CentsSquaredSats] = SeriesPattern18(client, _m(acc, 'capitalized_cap_in_profit_raw')) @@ -2938,7 +2960,7 @@ class BpsCentsPercentilesRatioSatsSmaStdUsdPattern: class Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.pct0_5: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct0_5') self.pct1: BpsPriceRatioPattern = BpsPriceRatioPattern(client, acc, 'pct1') @@ -2952,7 +2974,7 @@ class Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern: class _10y2y3y4y5y6y8yPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._10y: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, '10y')) self._2y: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, '2y')) @@ -2965,7 +2987,7 @@ class _10y2y3y4y5y6y8yPattern: class _1m1w1y24hBpsPercentRatioPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, '1m')) self._1w: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, '1w')) @@ -2982,7 +3004,7 @@ class ActiveInputOutputSpendablePattern: class CapLossMvrvNetPriceProfitSoprPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cap: CentsDeltaUsdPattern = CentsDeltaUsdPattern(client, _m(acc, 'realized_cap')) self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, _m(acc, 'realized_loss')) @@ -2995,7 +3017,7 @@ class CapLossMvrvNetPriceProfitSoprPattern: class InMaxMinPerSupplyPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.in_loss: PerPattern = PerPattern(client, _m(acc, 'cost_basis_in_loss_per')) self.in_profit: PerPattern = PerPattern(client, _m(acc, 'cost_basis_in_profit_per')) @@ -3008,7 +3030,7 @@ class InMaxMinPerSupplyPattern: class MaxMedianMinPct10Pct25Pct75Pct90Pattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.max: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'max')) self.median: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'median')) @@ -3021,7 +3043,7 @@ class MaxMedianMinPct10Pct25Pct75Pct90Pattern2: class MaxMedianMinPct10Pct25Pct75Pct90Pattern(Generic[T]): """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.max: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'max')) self.median: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'median')) @@ -3034,7 +3056,7 @@ class MaxMedianMinPct10Pct25Pct75Pct90Pattern(Generic[T]): class _1m1w1y2y4yAllPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: BpsRatioPattern2 = BpsRatioPattern2(client, _m(acc, '1m')) self._1w: BpsRatioPattern2 = BpsRatioPattern2(client, _m(acc, '1w')) @@ -3046,7 +3068,7 @@ class _1m1w1y2y4yAllPattern: class ActivityAddrOutputsRealizedSupplyUnrealizedPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.activity: TransferPattern = TransferPattern(client, _m(acc, 'transfer_volume')) self.addr_count: BaseDeltaPattern = BaseDeltaPattern(client, _m(acc, 'addr_count')) @@ -3058,7 +3080,7 @@ class ActivityAddrOutputsRealizedSupplyUnrealizedPattern: class AverageBlockCumulativeInSumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.average: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'average')) self.block: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, acc) @@ -3070,7 +3092,7 @@ class AverageBlockCumulativeInSumPattern: class BpsCentsPercentilesRatioSatsUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, _m(acc, 'ratio_bps')) self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) @@ -3082,7 +3104,7 @@ class BpsCentsPercentilesRatioSatsUsdPattern: class CentsNegativeToUsdPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) self.negative: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'neg')) @@ -3094,7 +3116,7 @@ class CentsNegativeToUsdPattern2: class DeltaDominanceHalfInTotalPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.delta: AbsoluteRatePattern3 = AbsoluteRatePattern3(client, _m(acc, 'delta')) self.dominance: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'dominance')) @@ -3106,7 +3128,7 @@ class DeltaDominanceHalfInTotalPattern2: class DeltaDominanceHalfInTotalPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.delta: AbsoluteRatePattern3 = AbsoluteRatePattern3(client, _m(acc, 'delta')) self.dominance: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'dominance')) @@ -3118,7 +3140,7 @@ class DeltaDominanceHalfInTotalPattern: class _1m1w1y24hBlockPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'average_1m')) self._1w: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'average_1w')) @@ -3129,7 +3151,7 @@ class _1m1w1y24hBlockPattern2: class _1m1w1y24hBlockPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'average_1m')) self._1w: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'average_1w')) @@ -3140,7 +3162,7 @@ class _1m1w1y24hBlockPattern: class ActiveBidirectionalReactivatedReceivingSendingPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.active: _1m1w1y24hBlockPattern = _1m1w1y24hBlockPattern(client, _m(acc, 'active_addrs')) self.bidirectional: _1m1w1y24hBlockPattern = _1m1w1y24hBlockPattern(client, _m(acc, 'bidirectional_addrs')) @@ -3151,7 +3173,7 @@ class ActiveBidirectionalReactivatedReceivingSendingPattern: class ActivityOutputsRealizedSupplyUnrealizedPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.activity: CoindaysTransferPattern = CoindaysTransferPattern(client, acc) self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, acc) @@ -3162,7 +3184,7 @@ class ActivityOutputsRealizedSupplyUnrealizedPattern: class ActivityOutputsRealizedSupplyUnrealizedPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.activity: TransferPattern = TransferPattern(client, _m(acc, 'transfer_volume')) self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, acc) @@ -3173,7 +3195,7 @@ class ActivityOutputsRealizedSupplyUnrealizedPattern3: class ActivityOutputsRealizedSupplyUnrealizedPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.activity: TransferPattern = TransferPattern(client, _m(acc, 'transfer_volume')) self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, acc) @@ -3184,7 +3206,7 @@ class ActivityOutputsRealizedSupplyUnrealizedPattern2: class BlockChangeCumulativeDeltaSumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.block: CentsUsdPattern4 = CentsUsdPattern4(client, _m(acc, 'realized_pnl')) self.change_1m: ToPattern = ToPattern(client, _m(acc, 'pnl_change_1m_to')) @@ -3195,7 +3217,7 @@ class BlockChangeCumulativeDeltaSumPattern: class BpsCentsRatioSatsUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, _m(acc, 'ratio_bps')) self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) @@ -3206,7 +3228,7 @@ class BpsCentsRatioSatsUsdPattern: class BtcCentsDeltaSatsUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc) self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) @@ -3217,7 +3239,7 @@ class BtcCentsDeltaSatsUsdPattern: class BtcCentsSatsShareUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc) self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) @@ -3228,7 +3250,7 @@ class BtcCentsSatsShareUsdPattern: class CapLossMvrvPriceProfitPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cap: CentsDeltaUsdPattern = CentsDeltaUsdPattern(client, _m(acc, 'realized_cap')) self.loss: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_loss')) @@ -3239,7 +3261,7 @@ class CapLossMvrvPriceProfitPattern: class CentsToUsdPattern4: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) self.to_mcap: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'to_mcap')) @@ -3254,7 +3276,7 @@ class EmaHistogramLineSignalPattern: class PhsReboundThsPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.phs: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'phs')) self.phs_min: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'phs_min')) @@ -3265,7 +3287,7 @@ class PhsReboundThsPattern: class _1m1w1y24hPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, '1m_rate')) self._1w: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, '1w_rate')) @@ -3275,7 +3297,7 @@ class _1m1w1y24hPattern2: class _1m1w1y24hPattern8: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, _m(acc, '1m')) self._1w: BpsPercentRatioPattern4 = BpsPercentRatioPattern4(client, _m(acc, '1w')) @@ -3285,7 +3307,7 @@ class _1m1w1y24hPattern8: class _1m1w1y24hPattern4: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, '1m')) self._1w: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, '1w')) @@ -3295,7 +3317,7 @@ class _1m1w1y24hPattern4: class _1m1w1y24hPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: BtcCentsSatsUsdPattern2 = BtcCentsSatsUsdPattern2(client, _m(acc, '1m')) self._1w: BtcCentsSatsUsdPattern2 = BtcCentsSatsUsdPattern2(client, _m(acc, '1w')) @@ -3305,7 +3327,7 @@ class _1m1w1y24hPattern3: class _1m1w1y24hPattern7: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: BtcSatsPattern = BtcSatsPattern(client, _m(acc, '1m')) self._1w: BtcSatsPattern = BtcSatsPattern(client, _m(acc, '1w')) @@ -3315,7 +3337,7 @@ class _1m1w1y24hPattern7: class _1m1w1y2wPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, '1m')) self._1w: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, '1w')) @@ -3325,7 +3347,7 @@ class _1m1w1y2wPattern: class _1m1w1y24hPattern5: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: CentsUsdPattern = CentsUsdPattern(client, _m(acc, '1m')) self._1w: CentsUsdPattern = CentsUsdPattern(client, _m(acc, '1w')) @@ -3335,7 +3357,7 @@ class _1m1w1y24hPattern5: class _1m1w1y24hPattern6: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, '1m')) self._1w: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, '1w')) @@ -3349,7 +3371,7 @@ class _1y2y4yAllPattern: class AverageBlockCumulativeSumPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.average: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, _m(acc, 'average')) self.block: SeriesPattern18[StoredU32] = SeriesPattern18(client, acc) @@ -3359,7 +3381,7 @@ class AverageBlockCumulativeSumPattern2: class AverageBlockCumulativeSumPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.average: _1m1w1y24hPattern3 = _1m1w1y24hPattern3(client, _m(acc, 'average')) self.block: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, acc) @@ -3369,7 +3391,7 @@ class AverageBlockCumulativeSumPattern3: class BlockCumulativeNegativeSumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.block: CentsUsdPattern2 = CentsUsdPattern2(client, acc) self.cumulative: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'cumulative')) @@ -3379,7 +3401,7 @@ class BlockCumulativeNegativeSumPattern: class BlockCumulativeDeltaSumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.block: CentsUsdPattern4 = CentsUsdPattern4(client, acc) self.cumulative: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'cumulative')) @@ -3389,7 +3411,7 @@ class BlockCumulativeDeltaSumPattern: class BtcCentsSatsUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc) self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) @@ -3399,7 +3421,7 @@ class BtcCentsSatsUsdPattern: class BtcCentsSatsUsdPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc) self.cents: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'cents')) @@ -3409,7 +3431,7 @@ class BtcCentsSatsUsdPattern2: class BtcCentsSatsUsdPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.btc: SeriesPattern18[Bitcoin] = SeriesPattern18(client, acc) self.cents: SeriesPattern18[Cents] = SeriesPattern18(client, _m(acc, 'cents')) @@ -3419,7 +3441,7 @@ class BtcCentsSatsUsdPattern3: class CentsDeltaToUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) self.delta: AbsoluteRatePattern2 = AbsoluteRatePattern2(client, _m(acc, 'delta')) @@ -3429,7 +3451,7 @@ class CentsDeltaToUsdPattern: class CentsToUsdPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[CentsSigned] = SeriesPattern1(client, _m(acc, 'cents')) self.to_own_gross_pnl: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'to_own_gross_pnl')) @@ -3439,7 +3461,7 @@ class CentsToUsdPattern3: class CoindaysCoinyearsDormancyTransferPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.coindays_destroyed: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'coindays_destroyed')) self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, _m(acc, 'coinyears_destroyed')) @@ -3449,7 +3471,7 @@ class CoindaysCoinyearsDormancyTransferPattern: class LossNetNuplProfitPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.loss: CentsNegativeUsdPattern = CentsNegativeUsdPattern(client, _m(acc, 'unrealized_loss')) self.net_pnl: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'net_unrealized_pnl')) @@ -3459,7 +3481,7 @@ class LossNetNuplProfitPattern: class NuplRealizedSupplyUnrealizedPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.nupl: BpsRatioPattern = BpsRatioPattern(client, _m(acc, 'nupl')) self.realized_cap: AllSthPattern = AllSthPattern(client, acc, 'realized_cap') @@ -3469,7 +3491,7 @@ class NuplRealizedSupplyUnrealizedPattern: class _1m1w1y24hPattern(Generic[T]): """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._1m: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, '1m')) self._1w: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, '1w')) @@ -3479,7 +3501,7 @@ class _1m1w1y24hPattern(Generic[T]): class AverageBlockCumulativeSumPattern(Generic[T]): """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.average: _1m1w1y24hPattern[T] = _1m1w1y24hPattern(client, _m(acc, 'average')) self.block: SeriesPattern18[T] = SeriesPattern18(client, acc) @@ -3489,7 +3511,7 @@ class AverageBlockCumulativeSumPattern(Generic[T]): class AdjustedRatioValuePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.adjusted: RatioTransferValuePattern = RatioTransferValuePattern(client, acc) self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, _m(acc, 'sopr')) @@ -3498,7 +3520,7 @@ class AdjustedRatioValuePattern: class BlockCumulativeSumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.block: CentsUsdPattern2 = CentsUsdPattern2(client, acc) self.cumulative: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'cumulative')) @@ -3507,7 +3529,7 @@ class BlockCumulativeSumPattern: class BlocksDominanceRewardsPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.blocks_mined: AverageBlockCumulativeSumPattern2 = AverageBlockCumulativeSumPattern2(client, _m(acc, 'blocks_mined')) self.dominance: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, _m(acc, 'dominance')) @@ -3516,7 +3538,7 @@ class BlocksDominanceRewardsPattern: class BpsPercentRatioPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPoints16] = SeriesPattern1(client, _m(acc, 'bps')) self.percent: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) @@ -3525,7 +3547,7 @@ class BpsPercentRatioPattern2: class BpsPercentRatioPattern4: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, _m(acc, 'bps')) self.percent: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) @@ -3534,7 +3556,7 @@ class BpsPercentRatioPattern4: class BpsPriceRatioPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str, disc: str): + def __init__(self, client: BrkClient, acc: str, disc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}_bps')) self.price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, disc)) @@ -3543,7 +3565,7 @@ class BpsPriceRatioPattern: class BpsPercentRatioPattern5: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPointsSigned16] = SeriesPattern1(client, _m(acc, 'bps')) self.percent: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) @@ -3552,7 +3574,7 @@ class BpsPercentRatioPattern5: class BpsPercentRatioPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPointsSigned32] = SeriesPattern1(client, _m(acc, 'bps')) self.percent: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) @@ -3561,7 +3583,7 @@ class BpsPercentRatioPattern: class CentsSatsUsdPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern2[Cents] = SeriesPattern2(client, _m(acc, 'cents')) self.sats: SeriesPattern2[Sats] = SeriesPattern2(client, _m(acc, 'sats')) @@ -3570,7 +3592,7 @@ class CentsSatsUsdPattern3: class CentsDeltaUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) self.delta: AbsoluteRatePattern2 = AbsoluteRatePattern2(client, _m(acc, 'delta')) @@ -3579,7 +3601,7 @@ class CentsDeltaUsdPattern: class CentsNegativeUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) self.negative: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, 'neg')) @@ -3588,7 +3610,7 @@ class CentsNegativeUsdPattern: class CentsSatsUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats')) @@ -3601,7 +3623,7 @@ class CountEventsSupplyPattern: class CumulativeRollingSumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cumulative: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'cumulative')) self.rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern = AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) @@ -3610,7 +3632,7 @@ class CumulativeRollingSumPattern: class DeltaDominanceTotalPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.delta: AbsoluteRatePattern3 = AbsoluteRatePattern3(client, _m(acc, 'delta')) self.dominance: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'dominance')) @@ -3619,7 +3641,7 @@ class DeltaDominanceTotalPattern: class GreedNetPainPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.greed_index: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'greed_index')) self.net: CentsUsdPattern = CentsUsdPattern(client, _m(acc, 'net_sentiment')) @@ -3628,7 +3650,7 @@ class GreedNetPainPattern: class LossNuplProfitPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.loss: CentsNegativeUsdPattern = CentsNegativeUsdPattern(client, _m(acc, 'unrealized_loss')) self.nupl: BpsRatioPattern = BpsRatioPattern(client, _m(acc, 'nupl')) @@ -3637,7 +3659,7 @@ class LossNuplProfitPattern: class RatioTransferValuePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, _m(acc, 'asopr')) self.transfer_volume: AverageBlockCumulativeSumPattern[Cents] = AverageBlockCumulativeSumPattern(client, _m(acc, 'adj_value_created')) @@ -3646,7 +3668,7 @@ class RatioTransferValuePattern: class RsiStochPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str, disc: str): + def __init__(self, client: BrkClient, acc: str, disc: str): """Create pattern node with accumulated series name.""" self.rsi: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, disc)) self.stoch_rsi_d: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, f'stoch_d_{disc}')) @@ -3655,7 +3677,7 @@ class RsiStochPattern: class SpendingSpentUnspentPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.spending_rate: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, 'spending_rate')) self.spent_count: AverageBlockCumulativeSumPattern2 = AverageBlockCumulativeSumPattern2(client, _m(acc, 'spent_utxo_count')) @@ -3664,7 +3686,7 @@ class SpendingSpentUnspentPattern: class _6bBlockTxPattern(Generic[T]): """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._6b: MaxMedianMinPct10Pct25Pct75Pct90Pattern[T] = MaxMedianMinPct10Pct25Pct75Pct90Pattern(client, _m(acc, '6b')) self.block: MaxMedianMinPct10Pct25Pct75Pct90Pattern[T] = MaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc) @@ -3673,7 +3695,7 @@ class _6bBlockTxPattern(Generic[T]): class AbsoluteRatePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.absolute: _1m1w1y24hPattern[StoredI64] = _1m1w1y24hPattern(client, acc) self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc) @@ -3681,7 +3703,7 @@ class AbsoluteRatePattern: class AbsoluteRatePattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.absolute: _1m1w1y24hPattern5 = _1m1w1y24hPattern5(client, acc) self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc) @@ -3689,7 +3711,7 @@ class AbsoluteRatePattern2: class AbsoluteRatePattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.absolute: _1m1w1y24hPattern7 = _1m1w1y24hPattern7(client, acc) self.rate: _1m1w1y24hPattern2 = _1m1w1y24hPattern2(client, acc) @@ -3697,7 +3719,7 @@ class AbsoluteRatePattern3: class AddrUtxoPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.addr: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'addr_amount')) self.utxo: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'utxo_amount')) @@ -3705,7 +3727,7 @@ class AddrUtxoPattern: class AllSthPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.all: BtcCentsDeltaSatsUsdPattern = BtcCentsDeltaSatsUsdPattern(client, _m(acc, 'supply')) self.sth: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'sth_supply')) @@ -3713,7 +3735,7 @@ class AllSthPattern2: class AllSthPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str, disc: str): + def __init__(self, client: BrkClient, acc: str, disc: str): """Create pattern node with accumulated series name.""" self.all: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, disc)) self.sth: SeriesPattern1[Dollars] = SeriesPattern1(client, _m(acc, f'sth_{disc}')) @@ -3721,7 +3743,7 @@ class AllSthPattern: class BaseSumPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.base: SeriesPattern18[Dollars] = SeriesPattern18(client, acc) self.sum: _1m1w1y24hPattern[Dollars] = _1m1w1y24hPattern(client, _m(acc, 'sum')) @@ -3729,7 +3751,7 @@ class BaseSumPattern: class BaseDeltaPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.base: SeriesPattern1[StoredU64] = SeriesPattern1(client, acc) self.delta: AbsoluteRatePattern = AbsoluteRatePattern(client, _m(acc, 'delta')) @@ -3737,7 +3759,7 @@ class BaseDeltaPattern: class BlockCumulativePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.block: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, acc) self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, _m(acc, 'cumulative')) @@ -3745,7 +3767,7 @@ class BlockCumulativePattern: class BlocksDominancePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.blocks_mined: AverageBlockCumulativeSumPattern2 = AverageBlockCumulativeSumPattern2(client, _m(acc, 'blocks_mined')) self.dominance: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'dominance')) @@ -3753,7 +3775,7 @@ class BlocksDominancePattern: class BpsRatioPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPoints32] = SeriesPattern1(client, _m(acc, 'bps')) self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) @@ -3761,7 +3783,7 @@ class BpsRatioPattern2: class BpsRatioPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.bps: SeriesPattern1[BasisPointsSigned32] = SeriesPattern1(client, _m(acc, 'bps')) self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, acc) @@ -3769,7 +3791,7 @@ class BpsRatioPattern: class BtcSatsPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.btc: SeriesPattern1[Bitcoin] = SeriesPattern1(client, acc) self.sats: SeriesPattern1[SatsSigned] = SeriesPattern1(client, _m(acc, 'sats')) @@ -3777,7 +3799,7 @@ class BtcSatsPattern: class CentsUsdPattern3: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, _m(acc, 'cents')) self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc) @@ -3785,7 +3807,7 @@ class CentsUsdPattern3: class CentsUsdPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern18[Cents] = SeriesPattern18(client, _m(acc, 'cents')) self.usd: SeriesPattern18[Dollars] = SeriesPattern18(client, acc) @@ -3793,7 +3815,7 @@ class CentsUsdPattern2: class CentsUsdPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern1[CentsSigned] = SeriesPattern1(client, _m(acc, 'cents')) self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc) @@ -3801,7 +3823,7 @@ class CentsUsdPattern: class CentsUsdPattern4: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.cents: SeriesPattern18[CentsSigned] = SeriesPattern18(client, _m(acc, 'cents')) self.usd: SeriesPattern18[Dollars] = SeriesPattern18(client, acc) @@ -3809,7 +3831,7 @@ class CentsUsdPattern4: class CoindaysTransferPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.coindays_destroyed: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'coindays_destroyed')) self.transfer_volume: AverageBlockCumulativeInSumPattern = AverageBlockCumulativeInSumPattern(client, _m(acc, 'transfer_volume')) @@ -3817,7 +3839,7 @@ class CoindaysTransferPattern: class FundedTotalPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.funded: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4 = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4(client, acc) self.total: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4 = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern4(client, _p('total', acc)) @@ -3825,7 +3847,7 @@ class FundedTotalPattern: class InPattern2: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.in_loss: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'loss')) self.in_profit: CentsUsdPattern3 = CentsUsdPattern3(client, _m(acc, 'profit')) @@ -3833,7 +3855,7 @@ class InPattern2: class InPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.in_loss: SharePattern = SharePattern(client, _m(acc, 'loss_share')) self.in_profit: SharePattern = SharePattern(client, _m(acc, 'profit_share')) @@ -3841,7 +3863,7 @@ class InPattern: class PerPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.per_coin: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'coin')) self.per_dollar: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, 'dollar')) @@ -3849,7 +3871,7 @@ class PerPattern: class PriceRatioPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str, disc: str): + def __init__(self, client: BrkClient, acc: str, disc: str): """Create pattern node with accumulated series name.""" self.price: CentsSatsUsdPattern = CentsSatsUsdPattern(client, _m(acc, disc)) self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, _m(acc, f'ratio_{disc}')) @@ -3857,7 +3879,7 @@ class PriceRatioPattern: class RatioValuePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.ratio: _24hPattern = _24hPattern(client, _m(acc, 'sopr_24h')) self.value_destroyed: AverageBlockCumulativeSumPattern[Cents] = AverageBlockCumulativeSumPattern(client, _m(acc, 'value_destroyed')) @@ -3869,7 +3891,7 @@ class SdSmaPattern: class ToPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.to_mcap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'mcap')) self.to_rcap: BpsPercentRatioPattern = BpsPercentRatioPattern(client, _m(acc, 'rcap')) @@ -3877,35 +3899,35 @@ class ToPattern: class _24hPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self._24h: SeriesPattern1[StoredF64] = SeriesPattern1(client, acc) class NuplPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.nupl: BpsRatioPattern = BpsRatioPattern(client, acc) class PricePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.price: BpsCentsPercentilesRatioSatsUsdPattern = BpsCentsPercentilesRatioSatsUsdPattern(client, acc) class SharePattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.share: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, acc) class TransferPattern: """Pattern struct for repeated tree structure.""" - def __init__(self, client: BrkClientBase, acc: str): + def __init__(self, client: BrkClient, acc: str): """Create pattern node with accumulated series name.""" self.transfer_volume: AverageBlockCumulativeSumPattern3 = AverageBlockCumulativeSumPattern3(client, acc) @@ -3914,7 +3936,7 @@ class TransferPattern: class SeriesTree_Blocks_Difficulty: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.value: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'difficulty') self.hashrate: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'difficulty_hashrate') self.adjustment: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'difficulty_adjustment') @@ -3925,13 +3947,13 @@ class SeriesTree_Blocks_Difficulty: class SeriesTree_Blocks_Time: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.timestamp: SeriesPattern18[Timestamp] = SeriesPattern18(client, 'timestamp') class SeriesTree_Blocks_Size: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.base: SeriesPattern18[StoredU64] = SeriesPattern18(client, 'total_size') self.cumulative: SeriesPattern1[StoredU64] = SeriesPattern1(client, 'block_size_cumulative') self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, 'block_size_sum') @@ -3947,14 +3969,14 @@ class SeriesTree_Blocks_Size: class SeriesTree_Blocks_Count: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.target: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, 'block_count_target') self.total: AverageBlockCumulativeSumPattern2 = AverageBlockCumulativeSumPattern2(client, 'block_count') class SeriesTree_Blocks_Lookback: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1h: SeriesPattern18[Height] = SeriesPattern18(client, 'height_1h_ago') self._24h: SeriesPattern18[Height] = SeriesPattern18(client, 'height_24h_ago') self._3d: SeriesPattern18[Height] = SeriesPattern18(client, 'height_3d_ago') @@ -4002,7 +4024,7 @@ class SeriesTree_Blocks_Lookback: class SeriesTree_Blocks_Interval: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.block: SeriesPattern18[Timestamp] = SeriesPattern18(client, 'block_interval') self._24h: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'block_interval_average_24h') self._1w: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'block_interval_average_1w') @@ -4012,7 +4034,7 @@ class SeriesTree_Blocks_Interval: class SeriesTree_Blocks_Fullness: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.bps: SeriesPattern18[BasisPoints16] = SeriesPattern18(client, 'block_fullness_bps') self.ratio: SeriesPattern18[StoredF32] = SeriesPattern18(client, 'block_fullness_ratio') self.percent: SeriesPattern18[StoredF32] = SeriesPattern18(client, 'block_fullness') @@ -4020,7 +4042,7 @@ class SeriesTree_Blocks_Fullness: class SeriesTree_Blocks_Halving: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.epoch: SeriesPattern1[Halving] = SeriesPattern1(client, 'halving_epoch') self.blocks_to_halving: SeriesPattern1[StoredU32] = SeriesPattern1(client, 'blocks_to_halving') self.days_to_halving: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'days_to_halving') @@ -4028,7 +4050,7 @@ class SeriesTree_Blocks_Halving: class SeriesTree_Blocks: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.blockhash: SeriesPattern18[BlockHash] = SeriesPattern18(client, 'blockhash') self.coinbase_tag: SeriesPattern18[CoinbaseTag] = SeriesPattern18(client, 'coinbase_tag') self.difficulty: SeriesTree_Blocks_Difficulty = SeriesTree_Blocks_Difficulty(client) @@ -4048,7 +4070,7 @@ class SeriesTree_Blocks: class SeriesTree_Transactions_Raw: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_tx_index: SeriesPattern18[TxIndex] = SeriesPattern18(client, 'first_tx_index') self.txid: SeriesPattern19[Txid] = SeriesPattern19(client, 'txid') self.tx_version: SeriesPattern19[TxVersion] = SeriesPattern19(client, 'tx_version') @@ -4062,13 +4084,13 @@ class SeriesTree_Transactions_Raw: class SeriesTree_Transactions_Count: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.total: AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern = AverageBlockCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, 'tx_count') class SeriesTree_Transactions_Size_Weight: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.tx_index: SeriesPattern19[Weight] = SeriesPattern19(client, 'tx_weight') self.block: MaxMedianMinPct10Pct25Pct75Pct90Pattern2 = MaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight') self._6b: MaxMedianMinPct10Pct25Pct75Pct90Pattern2 = MaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight_6b') @@ -4076,14 +4098,14 @@ class SeriesTree_Transactions_Size_Weight: class SeriesTree_Transactions_Size: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.vsize: _6bBlockTxPattern[VSize] = _6bBlockTxPattern(client, 'tx_vsize') self.weight: SeriesTree_Transactions_Size_Weight = SeriesTree_Transactions_Size_Weight(client) class SeriesTree_Transactions_Fees: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.input_value: SeriesPattern19[Sats] = SeriesPattern19(client, 'input_value') self.output_value: SeriesPattern19[Sats] = SeriesPattern19(client, 'output_value') self.fee: _6bBlockTxPattern[Sats] = _6bBlockTxPattern(client, 'fee') @@ -4093,7 +4115,7 @@ class SeriesTree_Transactions_Fees: class SeriesTree_Transactions_Versions: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.v1: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'tx_v1') self.v2: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'tx_v2') self.v3: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'tx_v3') @@ -4101,14 +4123,14 @@ class SeriesTree_Transactions_Versions: class SeriesTree_Transactions_Volume: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.transfer_volume: AverageBlockCumulativeSumPattern3 = AverageBlockCumulativeSumPattern3(client, 'transfer_volume_bis') self.tx_per_sec: _1m1w1y24hPattern[StoredF32] = _1m1w1y24hPattern(client, 'tx_per_sec') class SeriesTree_Transactions: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.raw: SeriesTree_Transactions_Raw = SeriesTree_Transactions_Raw(client) self.count: SeriesTree_Transactions_Count = SeriesTree_Transactions_Count(client) self.size: SeriesTree_Transactions_Size = SeriesTree_Transactions_Size(client) @@ -4119,7 +4141,7 @@ class SeriesTree_Transactions: class SeriesTree_Inputs_Raw: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_txin_index: SeriesPattern18[TxInIndex] = SeriesPattern18(client, 'first_txin_index') self.outpoint: SeriesPattern20[OutPoint] = SeriesPattern20(client, 'outpoint') self.tx_index: SeriesPattern20[TxIndex] = SeriesPattern20(client, 'tx_index') @@ -4129,14 +4151,14 @@ class SeriesTree_Inputs_Raw: class SeriesTree_Inputs_Spent: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.txout_index: SeriesPattern20[TxOutIndex] = SeriesPattern20(client, 'txout_index') self.value: SeriesPattern20[Sats] = SeriesPattern20(client, 'value') class SeriesTree_Inputs_ByType_InputCount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'input_count_bis') self.p2pk65: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'p2pk65_prevout_count') self.p2pk33: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'p2pk33_prevout_count') @@ -4153,7 +4175,7 @@ class SeriesTree_Inputs_ByType_InputCount: class SeriesTree_Inputs_ByType_InputShare: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.p2pk65: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'p2pk65_prevout_share') self.p2pk33: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'p2pk33_prevout_share') self.p2pkh: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'p2pkh_prevout_share') @@ -4169,7 +4191,7 @@ class SeriesTree_Inputs_ByType_InputShare: class SeriesTree_Inputs_ByType_TxCount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'non_coinbase_tx_count') self.p2pk65: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'tx_count_with_p2pk65_prevout') self.p2pk33: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'tx_count_with_p2pk33_prevout') @@ -4186,7 +4208,7 @@ class SeriesTree_Inputs_ByType_TxCount: class SeriesTree_Inputs_ByType: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.input_count: SeriesTree_Inputs_ByType_InputCount = SeriesTree_Inputs_ByType_InputCount(client) self.input_share: SeriesTree_Inputs_ByType_InputShare = SeriesTree_Inputs_ByType_InputShare(client) self.tx_count: SeriesTree_Inputs_ByType_TxCount = SeriesTree_Inputs_ByType_TxCount(client) @@ -4195,7 +4217,7 @@ class SeriesTree_Inputs_ByType: class SeriesTree_Inputs: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.raw: SeriesTree_Inputs_Raw = SeriesTree_Inputs_Raw(client) self.spent: SeriesTree_Inputs_Spent = SeriesTree_Inputs_Spent(client) self.count: CumulativeRollingSumPattern = CumulativeRollingSumPattern(client, 'input_count') @@ -4205,7 +4227,7 @@ class SeriesTree_Inputs: class SeriesTree_Outputs_Raw: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_txout_index: SeriesPattern18[TxOutIndex] = SeriesPattern18(client, 'first_txout_index') self.value: SeriesPattern21[Sats] = SeriesPattern21(client, 'value') self.output_type: SeriesPattern21[OutputType] = SeriesPattern21(client, 'output_type') @@ -4215,25 +4237,25 @@ class SeriesTree_Outputs_Raw: class SeriesTree_Outputs_Spent: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.txin_index: SeriesPattern21[TxInIndex] = SeriesPattern21(client, 'txin_index') class SeriesTree_Outputs_Count: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.total: CumulativeRollingSumPattern = CumulativeRollingSumPattern(client, 'output_count') class SeriesTree_Outputs_Unspent: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.count: SeriesPattern1[StoredU64] = SeriesPattern1(client, 'utxo_count_bis') class SeriesTree_Outputs_ByType_OutputCount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'output_count_bis') self.p2pk65: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'p2pk65_output_count') self.p2pk33: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'p2pk33_output_count') @@ -4251,7 +4273,7 @@ class SeriesTree_Outputs_ByType_OutputCount: class SeriesTree_Outputs_ByType_OutputShare: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.p2pk65: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'p2pk65_output_share') self.p2pk33: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'p2pk33_output_share') self.p2pkh: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'p2pkh_output_share') @@ -4268,7 +4290,7 @@ class SeriesTree_Outputs_ByType_OutputShare: class SeriesTree_Outputs_ByType: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.output_count: SeriesTree_Outputs_ByType_OutputCount = SeriesTree_Outputs_ByType_OutputCount(client) self.spendable_output_count: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'spendable_output_count') self.output_share: SeriesTree_Outputs_ByType_OutputShare = SeriesTree_Outputs_ByType_OutputShare(client) @@ -4278,13 +4300,13 @@ class SeriesTree_Outputs_ByType: class SeriesTree_Outputs_Value: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.op_return: BlockCumulativePattern = BlockCumulativePattern(client, 'op_return_value') class SeriesTree_Outputs: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.raw: SeriesTree_Outputs_Raw = SeriesTree_Outputs_Raw(client) self.spent: SeriesTree_Outputs_Spent = SeriesTree_Outputs_Spent(client) self.count: SeriesTree_Outputs_Count = SeriesTree_Outputs_Count(client) @@ -4296,63 +4318,63 @@ class SeriesTree_Outputs: class SeriesTree_Addrs_Raw_P2pk65: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2PK65AddrIndex] = SeriesPattern18(client, 'first_p2pk65_addr_index') self.bytes: SeriesPattern27[P2PK65Bytes] = SeriesPattern27(client, 'p2pk65_bytes') class SeriesTree_Addrs_Raw_P2pk33: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2PK33AddrIndex] = SeriesPattern18(client, 'first_p2pk33_addr_index') self.bytes: SeriesPattern26[P2PK33Bytes] = SeriesPattern26(client, 'p2pk33_bytes') class SeriesTree_Addrs_Raw_P2pkh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2PKHAddrIndex] = SeriesPattern18(client, 'first_p2pkh_addr_index') self.bytes: SeriesPattern28[P2PKHBytes] = SeriesPattern28(client, 'p2pkh_bytes') class SeriesTree_Addrs_Raw_P2sh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2SHAddrIndex] = SeriesPattern18(client, 'first_p2sh_addr_index') self.bytes: SeriesPattern29[P2SHBytes] = SeriesPattern29(client, 'p2sh_bytes') class SeriesTree_Addrs_Raw_P2wpkh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2WPKHAddrIndex] = SeriesPattern18(client, 'first_p2wpkh_addr_index') self.bytes: SeriesPattern31[P2WPKHBytes] = SeriesPattern31(client, 'p2wpkh_bytes') class SeriesTree_Addrs_Raw_P2wsh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2WSHAddrIndex] = SeriesPattern18(client, 'first_p2wsh_addr_index') self.bytes: SeriesPattern32[P2WSHBytes] = SeriesPattern32(client, 'p2wsh_bytes') class SeriesTree_Addrs_Raw_P2tr: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2TRAddrIndex] = SeriesPattern18(client, 'first_p2tr_addr_index') self.bytes: SeriesPattern30[P2TRBytes] = SeriesPattern30(client, 'p2tr_bytes') class SeriesTree_Addrs_Raw_P2a: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2AAddrIndex] = SeriesPattern18(client, 'first_p2a_addr_index') self.bytes: SeriesPattern24[P2ABytes] = SeriesPattern24(client, 'p2a_bytes') class SeriesTree_Addrs_Raw: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.p2pk65: SeriesTree_Addrs_Raw_P2pk65 = SeriesTree_Addrs_Raw_P2pk65(client) self.p2pk33: SeriesTree_Addrs_Raw_P2pk33 = SeriesTree_Addrs_Raw_P2pk33(client) self.p2pkh: SeriesTree_Addrs_Raw_P2pkh = SeriesTree_Addrs_Raw_P2pkh(client) @@ -4365,7 +4387,7 @@ class SeriesTree_Addrs_Raw: class SeriesTree_Addrs_Indexes: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.p2a: SeriesPattern24[AnyAddrIndex] = SeriesPattern24(client, 'any_addr_index') self.p2pk33: SeriesPattern26[AnyAddrIndex] = SeriesPattern26(client, 'any_addr_index') self.p2pk65: SeriesPattern27[AnyAddrIndex] = SeriesPattern27(client, 'any_addr_index') @@ -4380,14 +4402,14 @@ class SeriesTree_Addrs_Indexes: class SeriesTree_Addrs_Data: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.funded: SeriesPattern34[FundedAddrData] = SeriesPattern34(client, 'funded_addr_data') self.empty: SeriesPattern35[EmptyAddrData] = SeriesPattern35(client, 'empty_addr_data') class SeriesTree_Addrs_Activity_All: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.reactivated: _1m1w1y24hBlockPattern = _1m1w1y24hBlockPattern(client, 'reactivated_addrs') self.sending: _1m1w1y24hBlockPattern = _1m1w1y24hBlockPattern(client, 'sending_addrs') self.receiving: _1m1w1y24hBlockPattern = _1m1w1y24hBlockPattern(client, 'receiving_addrs') @@ -4397,7 +4419,7 @@ class SeriesTree_Addrs_Activity_All: class SeriesTree_Addrs_Activity: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: SeriesTree_Addrs_Activity_All = SeriesTree_Addrs_Activity_All(client) self.p2pk65: ActiveBidirectionalReactivatedReceivingSendingPattern = ActiveBidirectionalReactivatedReceivingSendingPattern(client, 'p2pk65') self.p2pk33: ActiveBidirectionalReactivatedReceivingSendingPattern = ActiveBidirectionalReactivatedReceivingSendingPattern(client, 'p2pk33') @@ -4411,7 +4433,7 @@ class SeriesTree_Addrs_Activity: class SeriesTree_Addrs_Reused_Events: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.output_to_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6(client, 'output_to_reused_addr_count') self.output_to_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7 = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7(client, 'output_to_reused_addr_share') self.spendable_output_to_reused_addr_share: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'spendable_output_to_reused_addr_share') @@ -4423,7 +4445,7 @@ class SeriesTree_Addrs_Reused_Events: class SeriesTree_Addrs_Reused_Supply: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'reused_addr_supply') self.p2pk65: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk65_reused_addr_supply') self.p2pk33: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk33_reused_addr_supply') @@ -4438,7 +4460,7 @@ class SeriesTree_Addrs_Reused_Supply: class SeriesTree_Addrs_Reused: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.count: FundedTotalPattern = FundedTotalPattern(client, 'reused_addr_count') self.events: SeriesTree_Addrs_Reused_Events = SeriesTree_Addrs_Reused_Events(client) self.supply: SeriesTree_Addrs_Reused_Supply = SeriesTree_Addrs_Reused_Supply(client) @@ -4446,7 +4468,7 @@ class SeriesTree_Addrs_Reused: class SeriesTree_Addrs_Respent_Events: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.output_to_reused_addr_count: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6 = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern6(client, 'output_to_respent_addr_count') self.output_to_reused_addr_share: AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7 = AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern7(client, 'output_to_respent_addr_share') self.spendable_output_to_reused_addr_share: _1m1w1y24hBpsPercentRatioPattern = _1m1w1y24hBpsPercentRatioPattern(client, 'spendable_output_to_respent_addr_share') @@ -4458,7 +4480,7 @@ class SeriesTree_Addrs_Respent_Events: class SeriesTree_Addrs_Respent_Supply: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'respent_addr_supply') self.p2pk65: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk65_respent_addr_supply') self.p2pk33: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk33_respent_addr_supply') @@ -4473,7 +4495,7 @@ class SeriesTree_Addrs_Respent_Supply: class SeriesTree_Addrs_Respent: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.count: FundedTotalPattern = FundedTotalPattern(client, 'respent_addr_count') self.events: SeriesTree_Addrs_Respent_Events = SeriesTree_Addrs_Respent_Events(client) self.supply: SeriesTree_Addrs_Respent_Supply = SeriesTree_Addrs_Respent_Supply(client) @@ -4481,7 +4503,7 @@ class SeriesTree_Addrs_Respent: class SeriesTree_Addrs_Exposed_Supply: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'exposed_addr_supply') self.p2pk65: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk65_exposed_addr_supply') self.p2pk33: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'p2pk33_exposed_addr_supply') @@ -4496,14 +4518,14 @@ class SeriesTree_Addrs_Exposed_Supply: class SeriesTree_Addrs_Exposed: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.count: FundedTotalPattern = FundedTotalPattern(client, 'exposed_addr_count') self.supply: SeriesTree_Addrs_Exposed_Supply = SeriesTree_Addrs_Exposed_Supply(client) class SeriesTree_Addrs_Delta: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: AbsoluteRatePattern = AbsoluteRatePattern(client, 'addr_count') self.p2pk65: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2pk65_addr_count') self.p2pk33: AbsoluteRatePattern = AbsoluteRatePattern(client, 'p2pk33_addr_count') @@ -4517,7 +4539,7 @@ class SeriesTree_Addrs_Delta: class SeriesTree_Addrs_AvgAmount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: AddrUtxoPattern = AddrUtxoPattern(client, 'avg') self.p2pk65: AddrUtxoPattern = AddrUtxoPattern(client, 'p2pk65_avg') self.p2pk33: AddrUtxoPattern = AddrUtxoPattern(client, 'p2pk33_avg') @@ -4531,7 +4553,7 @@ class SeriesTree_Addrs_AvgAmount: class SeriesTree_Addrs: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.raw: SeriesTree_Addrs_Raw = SeriesTree_Addrs_Raw(client) self.indexes: SeriesTree_Addrs_Indexes = SeriesTree_Addrs_Indexes(client) self.data: SeriesTree_Addrs_Data = SeriesTree_Addrs_Data(client) @@ -4549,35 +4571,35 @@ class SeriesTree_Addrs: class SeriesTree_Scripts_Raw_Empty: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[EmptyOutputIndex] = SeriesPattern18(client, 'first_empty_output_index') self.to_tx_index: SeriesPattern22[TxIndex] = SeriesPattern22(client, 'tx_index') class SeriesTree_Scripts_Raw_OpReturn: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[OpReturnIndex] = SeriesPattern18(client, 'first_op_return_index') self.to_tx_index: SeriesPattern23[TxIndex] = SeriesPattern23(client, 'tx_index') class SeriesTree_Scripts_Raw_P2ms: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[P2MSOutputIndex] = SeriesPattern18(client, 'first_p2ms_output_index') self.to_tx_index: SeriesPattern25[TxIndex] = SeriesPattern25(client, 'tx_index') class SeriesTree_Scripts_Raw_Unknown: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_index: SeriesPattern18[UnknownOutputIndex] = SeriesPattern18(client, 'first_unknown_output_index') self.to_tx_index: SeriesPattern33[TxIndex] = SeriesPattern33(client, 'tx_index') class SeriesTree_Scripts_Raw: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.empty: SeriesTree_Scripts_Raw_Empty = SeriesTree_Scripts_Raw_Empty(client) self.op_return: SeriesTree_Scripts_Raw_OpReturn = SeriesTree_Scripts_Raw_OpReturn(client) self.p2ms: SeriesTree_Scripts_Raw_P2ms = SeriesTree_Scripts_Raw_P2ms(client) @@ -4586,13 +4608,13 @@ class SeriesTree_Scripts_Raw: class SeriesTree_Scripts: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.raw: SeriesTree_Scripts_Raw = SeriesTree_Scripts_Raw(client) class SeriesTree_Mining_Rewards_Subsidy: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.block: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'subsidy') self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'subsidy_cumulative') self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'subsidy_sum') @@ -4602,7 +4624,7 @@ class SeriesTree_Mining_Rewards_Subsidy: class SeriesTree_Mining_Rewards_Fees_ToSubsidyRatio: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._24h: BpsRatioPattern2 = BpsRatioPattern2(client, 'fee_to_subsidy_ratio_24h') self._1w: BpsRatioPattern2 = BpsRatioPattern2(client, 'fee_to_subsidy_ratio_1w') self._1m: BpsRatioPattern2 = BpsRatioPattern2(client, 'fee_to_subsidy_ratio_1m') @@ -4611,7 +4633,7 @@ class SeriesTree_Mining_Rewards_Fees_ToSubsidyRatio: class SeriesTree_Mining_Rewards_Fees: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.block: BtcCentsSatsUsdPattern3 = BtcCentsSatsUsdPattern3(client, 'fees') self.cumulative: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'fees_cumulative') self.sum: _1m1w1y24hPattern4 = _1m1w1y24hPattern4(client, 'fees_sum') @@ -4629,7 +4651,7 @@ class SeriesTree_Mining_Rewards_Fees: class SeriesTree_Mining_Rewards: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.coinbase: AverageBlockCumulativeSumPattern3 = AverageBlockCumulativeSumPattern3(client, 'coinbase') self.subsidy: SeriesTree_Mining_Rewards_Subsidy = SeriesTree_Mining_Rewards_Subsidy(client) self.fees: SeriesTree_Mining_Rewards_Fees = SeriesTree_Mining_Rewards_Fees(client) @@ -4639,7 +4661,7 @@ class SeriesTree_Mining_Rewards: class SeriesTree_Mining_Hashrate_Rate_Sma: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1w: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'hash_rate_sma_1w') self._1m: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'hash_rate_sma_1m') self._2m: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'hash_rate_sma_2m') @@ -4648,7 +4670,7 @@ class SeriesTree_Mining_Hashrate_Rate_Sma: class SeriesTree_Mining_Hashrate_Rate: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.base: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'hash_rate') self.sma: SeriesTree_Mining_Hashrate_Rate_Sma = SeriesTree_Mining_Hashrate_Rate_Sma(client) self.ath: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'hash_rate_ath') @@ -4657,7 +4679,7 @@ class SeriesTree_Mining_Hashrate_Rate: class SeriesTree_Mining_Hashrate: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.rate: SeriesTree_Mining_Hashrate_Rate = SeriesTree_Mining_Hashrate_Rate(client) self.price: PhsReboundThsPattern = PhsReboundThsPattern(client, 'hash_price') self.value: PhsReboundThsPattern = PhsReboundThsPattern(client, 'hash_value') @@ -4665,14 +4687,14 @@ class SeriesTree_Mining_Hashrate: class SeriesTree_Mining: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.rewards: SeriesTree_Mining_Rewards = SeriesTree_Mining_Rewards(client) self.hashrate: SeriesTree_Mining_Hashrate = SeriesTree_Mining_Hashrate(client) class SeriesTree_Cointime_Activity: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.coinblocks_created: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'coinblocks_created') self.coinblocks_stored: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'coinblocks_stored') self.liveliness: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'liveliness') @@ -4683,14 +4705,14 @@ class SeriesTree_Cointime_Activity: class SeriesTree_Cointime_Supply: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.vaulted: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'vaulted_supply') self.active: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'active_supply') class SeriesTree_Cointime_Value: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.destroyed: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'cointime_value_destroyed') self.created: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'cointime_value_created') self.stored: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'cointime_value_stored') @@ -4699,7 +4721,7 @@ class SeriesTree_Cointime_Value: class SeriesTree_Cointime_Cap: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.thermo: CentsUsdPattern3 = CentsUsdPattern3(client, 'thermo_cap') self.investor: CentsUsdPattern3 = CentsUsdPattern3(client, 'investor_cap') self.vaulted: CentsUsdPattern3 = CentsUsdPattern3(client, 'vaulted_cap') @@ -4710,7 +4732,7 @@ class SeriesTree_Cointime_Cap: class SeriesTree_Cointime_Prices: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.vaulted: BpsCentsPercentilesRatioSatsUsdPattern = BpsCentsPercentilesRatioSatsUsdPattern(client, 'vaulted_price') self.active: BpsCentsPercentilesRatioSatsUsdPattern = BpsCentsPercentilesRatioSatsUsdPattern(client, 'active_price') self.true_market_mean: BpsCentsPercentilesRatioSatsUsdPattern = BpsCentsPercentilesRatioSatsUsdPattern(client, 'true_market_mean') @@ -4719,7 +4741,7 @@ class SeriesTree_Cointime_Prices: class SeriesTree_Cointime_Adjusted: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.inflation_rate: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'cointime_adj_inflation_rate') self.tx_velocity_native: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'cointime_adj_tx_velocity_btc') self.tx_velocity_fiat: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'cointime_adj_tx_velocity_usd') @@ -4727,7 +4749,7 @@ class SeriesTree_Cointime_Adjusted: class SeriesTree_Cointime_ReserveRisk: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.value: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'reserve_risk') self.vocdd_median_1y: SeriesPattern18[StoredF64] = SeriesPattern18(client, 'vocdd_median_1y') self.hodl_bank: SeriesPattern18[StoredF64] = SeriesPattern18(client, 'hodl_bank') @@ -4735,7 +4757,7 @@ class SeriesTree_Cointime_ReserveRisk: class SeriesTree_Cointime: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.activity: SeriesTree_Cointime_Activity = SeriesTree_Cointime_Activity(client) self.supply: SeriesTree_Cointime_Supply = SeriesTree_Cointime_Supply(client) self.value: SeriesTree_Cointime_Value = SeriesTree_Cointime_Value(client) @@ -4747,7 +4769,7 @@ class SeriesTree_Cointime: class SeriesTree_Constants: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._0: SeriesPattern1[StoredU16] = SeriesPattern1(client, 'constant_0') self._1: SeriesPattern1[StoredU16] = SeriesPattern1(client, 'constant_1') self._2: SeriesPattern1[StoredU16] = SeriesPattern1(client, 'constant_2') @@ -4770,87 +4792,87 @@ class SeriesTree_Constants: class SeriesTree_Indexes_Addr_P2pk33: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern26[P2PK33AddrIndex] = SeriesPattern26(client, 'p2pk33_addr_index') self.addr: SeriesPattern26[Addr] = SeriesPattern26(client, 'p2pk33_addr') class SeriesTree_Indexes_Addr_P2pk65: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern27[P2PK65AddrIndex] = SeriesPattern27(client, 'p2pk65_addr_index') self.addr: SeriesPattern27[Addr] = SeriesPattern27(client, 'p2pk65_addr') class SeriesTree_Indexes_Addr_P2pkh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern28[P2PKHAddrIndex] = SeriesPattern28(client, 'p2pkh_addr_index') self.addr: SeriesPattern28[Addr] = SeriesPattern28(client, 'p2pkh_addr') class SeriesTree_Indexes_Addr_P2sh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern29[P2SHAddrIndex] = SeriesPattern29(client, 'p2sh_addr_index') self.addr: SeriesPattern29[Addr] = SeriesPattern29(client, 'p2sh_addr') class SeriesTree_Indexes_Addr_P2tr: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern30[P2TRAddrIndex] = SeriesPattern30(client, 'p2tr_addr_index') self.addr: SeriesPattern30[Addr] = SeriesPattern30(client, 'p2tr_addr') class SeriesTree_Indexes_Addr_P2wpkh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern31[P2WPKHAddrIndex] = SeriesPattern31(client, 'p2wpkh_addr_index') self.addr: SeriesPattern31[Addr] = SeriesPattern31(client, 'p2wpkh_addr') class SeriesTree_Indexes_Addr_P2wsh: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern32[P2WSHAddrIndex] = SeriesPattern32(client, 'p2wsh_addr_index') self.addr: SeriesPattern32[Addr] = SeriesPattern32(client, 'p2wsh_addr') class SeriesTree_Indexes_Addr_P2a: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern24[P2AAddrIndex] = SeriesPattern24(client, 'p2a_addr_index') self.addr: SeriesPattern24[Addr] = SeriesPattern24(client, 'p2a_addr') class SeriesTree_Indexes_Addr_P2ms: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern25[P2MSOutputIndex] = SeriesPattern25(client, 'p2ms_output_index') class SeriesTree_Indexes_Addr_Empty: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern22[EmptyOutputIndex] = SeriesPattern22(client, 'empty_output_index') class SeriesTree_Indexes_Addr_Unknown: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern33[UnknownOutputIndex] = SeriesPattern33(client, 'unknown_output_index') class SeriesTree_Indexes_Addr_OpReturn: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern23[OpReturnIndex] = SeriesPattern23(client, 'op_return_index') class SeriesTree_Indexes_Addr: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.p2pk33: SeriesTree_Indexes_Addr_P2pk33 = SeriesTree_Indexes_Addr_P2pk33(client) self.p2pk65: SeriesTree_Indexes_Addr_P2pk65 = SeriesTree_Indexes_Addr_P2pk65(client) self.p2pkh: SeriesTree_Indexes_Addr_P2pkh = SeriesTree_Indexes_Addr_P2pkh(client) @@ -4867,7 +4889,7 @@ class SeriesTree_Indexes_Addr: class SeriesTree_Indexes_Height: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.minute10: SeriesPattern18[Minute10] = SeriesPattern18(client, 'minute10') self.minute30: SeriesPattern18[Minute30] = SeriesPattern18(client, 'minute30') self.hour1: SeriesPattern18[Hour1] = SeriesPattern18(client, 'hour1') @@ -4888,105 +4910,105 @@ class SeriesTree_Indexes_Height: class SeriesTree_Indexes_Epoch: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_height: SeriesPattern17[Height] = SeriesPattern17(client, 'first_height') class SeriesTree_Indexes_Halving: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_height: SeriesPattern16[Height] = SeriesPattern16(client, 'first_height') class SeriesTree_Indexes_Minute10: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_height: SeriesPattern3[Height] = SeriesPattern3(client, 'first_height') class SeriesTree_Indexes_Minute30: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_height: SeriesPattern4[Height] = SeriesPattern4(client, 'first_height') class SeriesTree_Indexes_Hour1: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_height: SeriesPattern5[Height] = SeriesPattern5(client, 'first_height') class SeriesTree_Indexes_Hour4: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_height: SeriesPattern6[Height] = SeriesPattern6(client, 'first_height') class SeriesTree_Indexes_Hour12: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.first_height: SeriesPattern7[Height] = SeriesPattern7(client, 'first_height') class SeriesTree_Indexes_Day1: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern8[Date] = SeriesPattern8(client, 'date') self.first_height: SeriesPattern8[Height] = SeriesPattern8(client, 'first_height') class SeriesTree_Indexes_Day3: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern9[Date] = SeriesPattern9(client, 'date') self.first_height: SeriesPattern9[Height] = SeriesPattern9(client, 'first_height') class SeriesTree_Indexes_Week1: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern10[Date] = SeriesPattern10(client, 'date') self.first_height: SeriesPattern10[Height] = SeriesPattern10(client, 'first_height') class SeriesTree_Indexes_Month1: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern11[Date] = SeriesPattern11(client, 'date') self.first_height: SeriesPattern11[Height] = SeriesPattern11(client, 'first_height') class SeriesTree_Indexes_Month3: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern12[Date] = SeriesPattern12(client, 'date') self.first_height: SeriesPattern12[Height] = SeriesPattern12(client, 'first_height') class SeriesTree_Indexes_Month6: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern13[Date] = SeriesPattern13(client, 'date') self.first_height: SeriesPattern13[Height] = SeriesPattern13(client, 'first_height') class SeriesTree_Indexes_Year1: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern14[Date] = SeriesPattern14(client, 'date') self.first_height: SeriesPattern14[Height] = SeriesPattern14(client, 'first_height') class SeriesTree_Indexes_Year10: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.date: SeriesPattern15[Date] = SeriesPattern15(client, 'date') self.first_height: SeriesPattern15[Height] = SeriesPattern15(client, 'first_height') class SeriesTree_Indexes_TxIndex: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern19[TxIndex] = SeriesPattern19(client, 'tx_index') self.input_count: SeriesPattern19[StoredU64] = SeriesPattern19(client, 'input_count') self.output_count: SeriesPattern19[StoredU64] = SeriesPattern19(client, 'output_count') @@ -4994,26 +5016,26 @@ class SeriesTree_Indexes_TxIndex: class SeriesTree_Indexes_TxinIndex: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern20[TxInIndex] = SeriesPattern20(client, 'txin_index') class SeriesTree_Indexes_TxoutIndex: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.identity: SeriesPattern21[TxOutIndex] = SeriesPattern21(client, 'txout_index') class SeriesTree_Indexes_Timestamp: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.monotonic: SeriesPattern18[Timestamp] = SeriesPattern18(client, 'timestamp_monotonic') self.resolutions: SeriesPattern2[Timestamp] = SeriesPattern2(client, 'timestamp') class SeriesTree_Indexes: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.addr: SeriesTree_Indexes_Addr = SeriesTree_Indexes_Addr(client) self.height: SeriesTree_Indexes_Height = SeriesTree_Indexes_Height(client) self.epoch: SeriesTree_Indexes_Epoch = SeriesTree_Indexes_Epoch(client) @@ -5039,14 +5061,14 @@ class SeriesTree_Indexes: class SeriesTree_Indicators_Dormancy: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.supply_adj: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'dormancy_supply_adj') self.flow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'dormancy_flow') class SeriesTree_Indicators_RarityMeter: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.full: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, 'rarity_meter') self.local: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, 'local_rarity_meter') self.cycle: IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern = IndexPct0Pct1Pct2Pct5Pct95Pct98Pct99ScorePattern(client, 'cycle_rarity_meter') @@ -5054,7 +5076,7 @@ class SeriesTree_Indicators_RarityMeter: class SeriesTree_Indicators: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.puell_multiple: BpsRatioPattern2 = BpsRatioPattern2(client, 'puell_multiple') self.nvt: BpsRatioPattern2 = BpsRatioPattern2(client, 'nvt') self.gini: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'gini') @@ -5070,7 +5092,7 @@ class SeriesTree_Indicators: class SeriesTree_Investing_Period_DcaCostBasis: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1w: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1w') self._1m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_1m') self._3m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_3m') @@ -5087,7 +5109,7 @@ class SeriesTree_Investing_Period_DcaCostBasis: class SeriesTree_Investing_Period: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.dca_stack: _10y1m1w1y2y3m3y4y5y6m6y8yPattern3 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, 'dca_stack') self.dca_cost_basis: SeriesTree_Investing_Period_DcaCostBasis = SeriesTree_Investing_Period_DcaCostBasis(client) self.dca_return: _10y1m1w1y2y3m3y4y5y6m6y8yPattern2 = _10y1m1w1y2y3m3y4y5y6m6y8yPattern2(client, 'dca_return') @@ -5098,7 +5120,7 @@ class SeriesTree_Investing_Period: class SeriesTree_Investing_Class_DcaStack: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.from_2015: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'dca_stack_from_2015') self.from_2016: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'dca_stack_from_2016') self.from_2017: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'dca_stack_from_2017') @@ -5115,7 +5137,7 @@ class SeriesTree_Investing_Class_DcaStack: class SeriesTree_Investing_Class_DcaCostBasis: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.from_2015: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2015') self.from_2016: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2016') self.from_2017: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'dca_cost_basis_from_2017') @@ -5132,7 +5154,7 @@ class SeriesTree_Investing_Class_DcaCostBasis: class SeriesTree_Investing_Class_DcaReturn: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.from_2015: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2015') self.from_2016: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2016') self.from_2017: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'dca_return_from_2017') @@ -5149,7 +5171,7 @@ class SeriesTree_Investing_Class_DcaReturn: class SeriesTree_Investing_Class: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.dca_stack: SeriesTree_Investing_Class_DcaStack = SeriesTree_Investing_Class_DcaStack(client) self.dca_cost_basis: SeriesTree_Investing_Class_DcaCostBasis = SeriesTree_Investing_Class_DcaCostBasis(client) self.dca_return: SeriesTree_Investing_Class_DcaReturn = SeriesTree_Investing_Class_DcaReturn(client) @@ -5157,7 +5179,7 @@ class SeriesTree_Investing_Class: class SeriesTree_Investing: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sats_per_day: SeriesPattern18[Sats] = SeriesPattern18(client, 'dca_sats_per_day') self.period: SeriesTree_Investing_Period = SeriesTree_Investing_Period(client) self.class_: SeriesTree_Investing_Class = SeriesTree_Investing_Class(client) @@ -5165,7 +5187,7 @@ class SeriesTree_Investing: class SeriesTree_Market_Ath: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.high: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'price_ath') self.drawdown: BpsPercentRatioPattern5 = BpsPercentRatioPattern5(client, 'price_drawdown') self.days_since: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'days_since_price_ath') @@ -5176,7 +5198,7 @@ class SeriesTree_Market_Ath: class SeriesTree_Market_Lookback: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._24h: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'price_past_24h') self._1w: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'price_past_1w') self._1m: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'price_past_1m') @@ -5194,7 +5216,7 @@ class SeriesTree_Market_Lookback: class SeriesTree_Market_Returns_Periods: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._24h: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'price_return_24h') self._1w: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'price_return_1w') self._1m: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'price_return_1m') @@ -5212,35 +5234,35 @@ class SeriesTree_Market_Returns_Periods: class SeriesTree_Market_Returns_Sd24h_24h: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sma: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sma_24h') self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sd_24h') class SeriesTree_Market_Returns_Sd24h_1w: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sma: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sma_1w') self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sd_1w') class SeriesTree_Market_Returns_Sd24h_1m: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sma: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sma_1m') self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sd_1m') class SeriesTree_Market_Returns_Sd24h_1y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sma: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sma_1y') self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_return_24h_sd_1y') class SeriesTree_Market_Returns_Sd24h: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._24h: SeriesTree_Market_Returns_Sd24h_24h = SeriesTree_Market_Returns_Sd24h_24h(client) self._1w: SeriesTree_Market_Returns_Sd24h_1w = SeriesTree_Market_Returns_Sd24h_1w(client) self._1m: SeriesTree_Market_Returns_Sd24h_1m = SeriesTree_Market_Returns_Sd24h_1m(client) @@ -5249,7 +5271,7 @@ class SeriesTree_Market_Returns_Sd24h: class SeriesTree_Market_Returns: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.periods: SeriesTree_Market_Returns_Periods = SeriesTree_Market_Returns_Periods(client) self.cagr: _10y2y3y4y5y6y8yPattern = _10y2y3y4y5y6y8yPattern(client, 'price_cagr') self.sd_24h: SeriesTree_Market_Returns_Sd24h = SeriesTree_Market_Returns_Sd24h(client) @@ -5257,7 +5279,7 @@ class SeriesTree_Market_Returns: class SeriesTree_Market_Range: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.min: _1m1w1y2wPattern = _1m1w1y2wPattern(client, 'price_min') self.max: _1m1w1y2wPattern = _1m1w1y2wPattern(client, 'price_max') self.true_range: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'price_true_range') @@ -5267,7 +5289,7 @@ class SeriesTree_Market_Range: class SeriesTree_Market_MovingAverage_Sma_200d: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'price_sma_200d') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'price_sma_200d_cents') self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'price_sma_200d_sats') @@ -5279,7 +5301,7 @@ class SeriesTree_Market_MovingAverage_Sma_200d: class SeriesTree_Market_MovingAverage_Sma_350d: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'price_sma_350d') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'price_sma_350d_cents') self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'price_sma_350d_sats') @@ -5290,7 +5312,7 @@ class SeriesTree_Market_MovingAverage_Sma_350d: class SeriesTree_Market_MovingAverage_Sma: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1w: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, 'price_sma_1w') self._8d: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, 'price_sma_8d') self._13d: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, 'price_sma_13d') @@ -5311,7 +5333,7 @@ class SeriesTree_Market_MovingAverage_Sma: class SeriesTree_Market_MovingAverage_Ema: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1w: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, 'price_ema_1w') self._8d: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, 'price_ema_8d') self._12d: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, 'price_ema_12d') @@ -5332,14 +5354,14 @@ class SeriesTree_Market_MovingAverage_Ema: class SeriesTree_Market_MovingAverage: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sma: SeriesTree_Market_MovingAverage_Sma = SeriesTree_Market_MovingAverage_Sma(client) self.ema: SeriesTree_Market_MovingAverage_Ema = SeriesTree_Market_MovingAverage_Ema(client) class SeriesTree_Market_Technical_Rsi: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._24h: RsiStochPattern = RsiStochPattern(client, 'rsi', '24h') self._1w: RsiStochPattern = RsiStochPattern(client, 'rsi', '1w') self._1m: RsiStochPattern = RsiStochPattern(client, 'rsi', '1m') @@ -5347,7 +5369,7 @@ class SeriesTree_Market_Technical_Rsi: class SeriesTree_Market_Technical_Macd_24h: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.ema_fast: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_ema_fast_24h') self.ema_slow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_ema_slow_24h') self.line: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_line_24h') @@ -5357,7 +5379,7 @@ class SeriesTree_Market_Technical_Macd_24h: class SeriesTree_Market_Technical_Macd_1w: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.ema_fast: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_ema_fast_1w') self.ema_slow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_ema_slow_1w') self.line: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_line_1w') @@ -5367,7 +5389,7 @@ class SeriesTree_Market_Technical_Macd_1w: class SeriesTree_Market_Technical_Macd_1m: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.ema_fast: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_ema_fast_1m') self.ema_slow: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_ema_slow_1m') self.line: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'macd_line_1m') @@ -5377,7 +5399,7 @@ class SeriesTree_Market_Technical_Macd_1m: class SeriesTree_Market_Technical_Macd: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._24h: SeriesTree_Market_Technical_Macd_24h = SeriesTree_Market_Technical_Macd_24h(client) self._1w: SeriesTree_Market_Technical_Macd_1w = SeriesTree_Market_Technical_Macd_1w(client) self._1m: SeriesTree_Market_Technical_Macd_1m = SeriesTree_Market_Technical_Macd_1m(client) @@ -5385,7 +5407,7 @@ class SeriesTree_Market_Technical_Macd: class SeriesTree_Market_Technical: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.rsi: SeriesTree_Market_Technical_Rsi = SeriesTree_Market_Technical_Rsi(client) self.pi_cycle: BpsRatioPattern2 = BpsRatioPattern2(client, 'pi_cycle') self.macd: SeriesTree_Market_Technical_Macd = SeriesTree_Market_Technical_Macd(client) @@ -5393,7 +5415,7 @@ class SeriesTree_Market_Technical: class SeriesTree_Market: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.ath: SeriesTree_Market_Ath = SeriesTree_Market_Ath(client) self.lookback: SeriesTree_Market_Lookback = SeriesTree_Market_Lookback(client) self.returns: SeriesTree_Market_Returns = SeriesTree_Market_Returns(client) @@ -5405,7 +5427,7 @@ class SeriesTree_Market: class SeriesTree_Pools_Major: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.unknown: BlocksDominanceRewardsPattern = BlocksDominanceRewardsPattern(client, 'unknown') self.luxor: BlocksDominanceRewardsPattern = BlocksDominanceRewardsPattern(client, 'luxor') self.btccom: BlocksDominanceRewardsPattern = BlocksDominanceRewardsPattern(client, 'btccom') @@ -5432,7 +5454,7 @@ class SeriesTree_Pools_Major: class SeriesTree_Pools_Minor: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.blockfills: BlocksDominancePattern = BlocksDominancePattern(client, 'blockfills') self.ultimuspool: BlocksDominancePattern = BlocksDominancePattern(client, 'ultimuspool') self.terrapool: BlocksDominancePattern = BlocksDominancePattern(client, 'terrapool') @@ -5580,7 +5602,7 @@ class SeriesTree_Pools_Minor: class SeriesTree_Pools: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.pool: SeriesPattern18[PoolSlug] = SeriesPattern18(client, 'pool') self.major: SeriesTree_Pools_Major = SeriesTree_Pools_Major(client) self.minor: SeriesTree_Pools_Minor = SeriesTree_Pools_Minor(client) @@ -5588,7 +5610,7 @@ class SeriesTree_Pools: class SeriesTree_Prices_Split: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.open: CentsSatsUsdPattern3 = CentsSatsUsdPattern3(client, 'price_open') self.high: CentsSatsUsdPattern3 = CentsSatsUsdPattern3(client, 'price_high') self.low: CentsSatsUsdPattern3 = CentsSatsUsdPattern3(client, 'price_low') @@ -5597,7 +5619,7 @@ class SeriesTree_Prices_Split: class SeriesTree_Prices_Ohlc: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern2[OHLCDollars] = SeriesPattern2(client, 'price_ohlc') self.cents: SeriesPattern2[OHLCCents] = SeriesPattern2(client, 'price_ohlc_cents') self.sats: SeriesPattern2[OHLCSats] = SeriesPattern2(client, 'price_ohlc_sats') @@ -5605,7 +5627,7 @@ class SeriesTree_Prices_Ohlc: class SeriesTree_Prices_Spot: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'price') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'price_cents') self.sats: SeriesPattern1[Sats] = SeriesPattern1(client, 'price_sats') @@ -5613,7 +5635,7 @@ class SeriesTree_Prices_Spot: class SeriesTree_Prices: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.split: SeriesTree_Prices_Split = SeriesTree_Prices_Split(client) self.ohlc: SeriesTree_Prices_Ohlc = SeriesTree_Prices_Ohlc(client) self.spot: SeriesTree_Prices_Spot = SeriesTree_Prices_Spot(client) @@ -5621,14 +5643,14 @@ class SeriesTree_Prices: class SeriesTree_Supply_Velocity: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.native: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'velocity_btc') self.fiat: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'velocity_usd') class SeriesTree_Supply: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.state: SeriesPattern18[SupplyState] = SeriesPattern18(client, 'supply_state') self.circulating: BtcCentsSatsUsdPattern = BtcCentsSatsUsdPattern(client, 'circulating_supply') self.burned: BlockCumulativePattern = BlockCumulativePattern(client, 'unspendable_supply') @@ -5641,7 +5663,7 @@ class SeriesTree_Supply: class SeriesTree_Cohorts_Utxo_All_Outputs: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.unspent_count: BaseDeltaPattern = BaseDeltaPattern(client, 'utxo_count') self.spent_count: AverageBlockCumulativeSumPattern2 = AverageBlockCumulativeSumPattern2(client, 'spent_utxo_count') self.spending_rate: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'spending_rate') @@ -5649,7 +5671,7 @@ class SeriesTree_Cohorts_Utxo_All_Outputs: class SeriesTree_Cohorts_Utxo_All_Activity: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.transfer_volume: AverageBlockCumulativeInSumPattern = AverageBlockCumulativeInSumPattern(client, 'transfer_volume') self.coindays_destroyed: AverageBlockCumulativeSumPattern[StoredF64] = AverageBlockCumulativeSumPattern(client, 'coindays_destroyed') self.coinyears_destroyed: SeriesPattern1[StoredF64] = SeriesPattern1(client, 'coinyears_destroyed') @@ -5658,7 +5680,7 @@ class SeriesTree_Cohorts_Utxo_All_Activity: class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd') @@ -5678,7 +5700,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All: class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd_4y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore_4y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd_4y') @@ -5698,7 +5720,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y: class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd_2y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore_2y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd_2y') @@ -5718,7 +5740,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y: class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_sd_1y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'realized_price_ratio_zscore_1y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'realized_price_0sd_1y') @@ -5738,7 +5760,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_1y: class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_All(client) self._4y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_4y(client) self._2y: SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev_2y(client) @@ -5747,7 +5769,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price_StdDev: class SeriesTree_Cohorts_Utxo_All_Realized_Price: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'realized_price') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'realized_price_cents') self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'realized_price_sats') @@ -5760,7 +5782,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Price: class SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'asopr') self.transfer_volume: AverageBlockCumulativeSumPattern[Cents] = AverageBlockCumulativeSumPattern(client, 'adj_value_created') self.value_destroyed: AverageBlockCumulativeSumPattern[Cents] = AverageBlockCumulativeSumPattern(client, 'adj_value_destroyed') @@ -5768,7 +5790,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted: class SeriesTree_Cohorts_Utxo_All_Realized_Sopr: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.value_destroyed: AverageBlockCumulativeSumPattern[Cents] = AverageBlockCumulativeSumPattern(client, 'value_destroyed') self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'sopr') self.adjusted: SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted = SeriesTree_Cohorts_Utxo_All_Realized_Sopr_Adjusted(client) @@ -5776,7 +5798,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized_Sopr: class SeriesTree_Cohorts_Utxo_All_Realized: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'realized_cap') self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'realized_profit') self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'realized_loss') @@ -5793,7 +5815,7 @@ class SeriesTree_Cohorts_Utxo_All_Realized: class SeriesTree_Cohorts_Utxo_All_CostBasis: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.in_profit: PerPattern = PerPattern(client, 'cost_basis_in_profit_per') self.in_loss: PerPattern = PerPattern(client, 'cost_basis_in_loss_per') self.min: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'cost_basis_min') @@ -5805,7 +5827,7 @@ class SeriesTree_Cohorts_Utxo_All_CostBasis: class SeriesTree_Cohorts_Utxo_All_Unrealized_Profit: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'unrealized_profit') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'unrealized_profit_cents') self.to_mcap: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'unrealized_profit_to_mcap') @@ -5814,7 +5836,7 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized_Profit: class SeriesTree_Cohorts_Utxo_All_Unrealized_Loss: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'unrealized_loss') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'unrealized_loss_cents') self.negative: SeriesPattern1[Dollars] = SeriesPattern1(client, 'unrealized_loss_neg') @@ -5824,7 +5846,7 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized_Loss: class SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'net_unrealized_pnl') self.cents: SeriesPattern1[CentsSigned] = SeriesPattern1(client, 'net_unrealized_pnl_cents') self.to_own_gross_pnl: BpsPercentRatioPattern = BpsPercentRatioPattern(client, 'net_unrealized_pnl_to_own_gross_pnl') @@ -5832,7 +5854,7 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized_NetPnl: class SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.pain_index: CentsUsdPattern3 = CentsUsdPattern3(client, 'pain_index') self.greed_index: CentsUsdPattern3 = CentsUsdPattern3(client, 'greed_index') self.net: CentsUsdPattern = CentsUsdPattern(client, 'net_sentiment') @@ -5840,7 +5862,7 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized_Sentiment: class SeriesTree_Cohorts_Utxo_All_Unrealized: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.nupl: BpsRatioPattern = BpsRatioPattern(client, 'nupl') self.profit: SeriesTree_Cohorts_Utxo_All_Unrealized_Profit = SeriesTree_Cohorts_Utxo_All_Unrealized_Profit(client) self.loss: SeriesTree_Cohorts_Utxo_All_Unrealized_Loss = SeriesTree_Cohorts_Utxo_All_Unrealized_Loss(client) @@ -5854,7 +5876,7 @@ class SeriesTree_Cohorts_Utxo_All_Unrealized: class SeriesTree_Cohorts_Utxo_All: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'supply') self.outputs: SeriesTree_Cohorts_Utxo_All_Outputs = SeriesTree_Cohorts_Utxo_All_Outputs(client) self.activity: SeriesTree_Cohorts_Utxo_All_Activity = SeriesTree_Cohorts_Utxo_All_Activity(client) @@ -5866,7 +5888,7 @@ class SeriesTree_Cohorts_Utxo_All: class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd') @@ -5886,7 +5908,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All: class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd_4y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore_4y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd_4y') @@ -5906,7 +5928,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y: class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd_2y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore_2y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd_2y') @@ -5926,7 +5948,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y: class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_sd_1y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'sth_realized_price_ratio_zscore_1y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'sth_realized_price_0sd_1y') @@ -5946,7 +5968,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_1y: class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_All(client) self._4y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_4y(client) self._2y: SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev_2y(client) @@ -5955,7 +5977,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price_StdDev: class SeriesTree_Cohorts_Utxo_Sth_Realized_Price: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'sth_realized_price') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'sth_realized_price_cents') self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'sth_realized_price_sats') @@ -5968,7 +5990,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized_Price: class SeriesTree_Cohorts_Utxo_Sth_Realized: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'sth_realized_cap') self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'sth_realized_profit') self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'sth_realized_loss') @@ -5985,7 +6007,7 @@ class SeriesTree_Cohorts_Utxo_Sth_Realized: class SeriesTree_Cohorts_Utxo_Sth: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'sth_supply') self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, 'sth') self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'sth') @@ -5997,7 +6019,7 @@ class SeriesTree_Cohorts_Utxo_Sth: class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd') @@ -6017,7 +6039,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All: class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd_4y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore_4y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd_4y') @@ -6037,7 +6059,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y: class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd_2y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore_2y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd_2y') @@ -6057,7 +6079,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y: class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.sd: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_sd_1y') self.zscore: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'lth_realized_price_ratio_zscore_1y') self._0sd: CentsSatsUsdPattern = CentsSatsUsdPattern(client, 'lth_realized_price_0sd_1y') @@ -6077,7 +6099,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_1y: class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_All(client) self._4y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_4y(client) self._2y: SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y = SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev_2y(client) @@ -6086,7 +6108,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price_StdDev: class SeriesTree_Cohorts_Utxo_Lth_Realized_Price: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, 'lth_realized_price') self.cents: SeriesPattern1[Cents] = SeriesPattern1(client, 'lth_realized_price_cents') self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, 'lth_realized_price_sats') @@ -6099,14 +6121,14 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized_Price: class SeriesTree_Cohorts_Utxo_Lth_Realized_Sopr: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.value_destroyed: AverageBlockCumulativeSumPattern[Cents] = AverageBlockCumulativeSumPattern(client, 'lth_value_destroyed') self.ratio: _1m1w1y24hPattern[StoredF64] = _1m1w1y24hPattern(client, 'lth_sopr') class SeriesTree_Cohorts_Utxo_Lth_Realized: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.cap: CentsDeltaToUsdPattern = CentsDeltaToUsdPattern(client, 'lth_realized_cap') self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, 'lth_realized_profit') self.loss: BlockCumulativeNegativeSumPattern = BlockCumulativeNegativeSumPattern(client, 'lth_realized_loss') @@ -6123,7 +6145,7 @@ class SeriesTree_Cohorts_Utxo_Lth_Realized: class SeriesTree_Cohorts_Utxo_Lth: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.supply: DeltaDominanceHalfInTotalPattern2 = DeltaDominanceHalfInTotalPattern2(client, 'lth_supply') self.outputs: SpendingSpentUnspentPattern = SpendingSpentUnspentPattern(client, 'lth') self.activity: CoindaysCoinyearsDormancyTransferPattern = CoindaysCoinyearsDormancyTransferPattern(client, 'lth') @@ -6135,7 +6157,7 @@ class SeriesTree_Cohorts_Utxo_Lth: class SeriesTree_Cohorts_Utxo_AgeRange: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.under_1h: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_under_1h_old') self._1h_to_1d: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_1h_to_1d_old') self._1d_to_1w: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_1d_to_1w_old') @@ -6161,7 +6183,7 @@ class SeriesTree_Cohorts_Utxo_AgeRange: class SeriesTree_Cohorts_Utxo_UnderAge: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1w: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_under_1w_old') self._1m: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_under_1m_old') self._2m: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_under_2m_old') @@ -6184,7 +6206,7 @@ class SeriesTree_Cohorts_Utxo_UnderAge: class SeriesTree_Cohorts_Utxo_OverAge: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1d: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_over_1d_old') self._1w: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_over_1w_old') self._1m: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'utxos_over_1m_old') @@ -6207,7 +6229,7 @@ class SeriesTree_Cohorts_Utxo_OverAge: class SeriesTree_Cohorts_Utxo_Epoch: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._0: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'epoch_0') self._1: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'epoch_1') self._2: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'epoch_2') @@ -6217,7 +6239,7 @@ class SeriesTree_Cohorts_Utxo_Epoch: class SeriesTree_Cohorts_Utxo_Class: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._2009: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'class_2009') self._2010: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'class_2010') self._2011: ActivityOutputsRealizedSupplyUnrealizedPattern = ActivityOutputsRealizedSupplyUnrealizedPattern(client, 'class_2011') @@ -6240,7 +6262,7 @@ class SeriesTree_Cohorts_Utxo_Class: class SeriesTree_Cohorts_Utxo_OverAmount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1sat: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_over_1sat') self._10sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_over_10sats') self._100sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_over_100sats') @@ -6258,7 +6280,7 @@ class SeriesTree_Cohorts_Utxo_OverAmount: class SeriesTree_Cohorts_Utxo_AmountRange: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._0sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_0sats') self._1sat_to_10sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_1sat_to_10sats') self._10sats_to_100sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_10sats_to_100sats') @@ -6278,7 +6300,7 @@ class SeriesTree_Cohorts_Utxo_AmountRange: class SeriesTree_Cohorts_Utxo_UnderAmount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._10sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_under_10sats') self._100sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_under_100sats') self._1k_sats: ActivityOutputsRealizedSupplyUnrealizedPattern2 = ActivityOutputsRealizedSupplyUnrealizedPattern2(client, 'utxos_under_1k_sats') @@ -6296,7 +6318,7 @@ class SeriesTree_Cohorts_Utxo_UnderAmount: class SeriesTree_Cohorts_Utxo_Type: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.p2pk65: ActivityOutputsRealizedSupplyUnrealizedPattern3 = ActivityOutputsRealizedSupplyUnrealizedPattern3(client, 'p2pk65') self.p2pk33: ActivityOutputsRealizedSupplyUnrealizedPattern3 = ActivityOutputsRealizedSupplyUnrealizedPattern3(client, 'p2pk33') self.p2pkh: ActivityOutputsRealizedSupplyUnrealizedPattern3 = ActivityOutputsRealizedSupplyUnrealizedPattern3(client, 'p2pkh') @@ -6312,7 +6334,7 @@ class SeriesTree_Cohorts_Utxo_Type: class SeriesTree_Cohorts_Utxo_Profitability_Range: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.over_1000pct_in_profit: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_over_1000pct_in_profit') self._500pct_to_1000pct_in_profit: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_500pct_to_1000pct_in_profit') self._300pct_to_500pct_in_profit: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_300pct_to_500pct_in_profit') @@ -6342,7 +6364,7 @@ class SeriesTree_Cohorts_Utxo_Profitability_Range: class SeriesTree_Cohorts_Utxo_Profitability_Profit: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_in_profit') self._10pct: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_over_10pct_in_profit') self._20pct: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_over_20pct_in_profit') @@ -6361,7 +6383,7 @@ class SeriesTree_Cohorts_Utxo_Profitability_Profit: class SeriesTree_Cohorts_Utxo_Profitability_Loss: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_in_loss') self._10pct: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_over_10pct_in_loss') self._20pct: NuplRealizedSupplyUnrealizedPattern = NuplRealizedSupplyUnrealizedPattern(client, 'utxos_over_20pct_in_loss') @@ -6375,7 +6397,7 @@ class SeriesTree_Cohorts_Utxo_Profitability_Loss: class SeriesTree_Cohorts_Utxo_Profitability: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.range: SeriesTree_Cohorts_Utxo_Profitability_Range = SeriesTree_Cohorts_Utxo_Profitability_Range(client) self.profit: SeriesTree_Cohorts_Utxo_Profitability_Profit = SeriesTree_Cohorts_Utxo_Profitability_Profit(client) self.loss: SeriesTree_Cohorts_Utxo_Profitability_Loss = SeriesTree_Cohorts_Utxo_Profitability_Loss(client) @@ -6383,7 +6405,7 @@ class SeriesTree_Cohorts_Utxo_Profitability: class SeriesTree_Cohorts_Utxo_Matured: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.under_1h: AverageBlockCumulativeSumPattern3 = AverageBlockCumulativeSumPattern3(client, 'utxos_under_1h_old_matured_supply') self._1h_to_1d: AverageBlockCumulativeSumPattern3 = AverageBlockCumulativeSumPattern3(client, 'utxos_1h_to_1d_old_matured_supply') self._1d_to_1w: AverageBlockCumulativeSumPattern3 = AverageBlockCumulativeSumPattern3(client, 'utxos_1d_to_1w_old_matured_supply') @@ -6409,7 +6431,7 @@ class SeriesTree_Cohorts_Utxo_Matured: class SeriesTree_Cohorts_Utxo: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.all: SeriesTree_Cohorts_Utxo_All = SeriesTree_Cohorts_Utxo_All(client) self.sth: SeriesTree_Cohorts_Utxo_Sth = SeriesTree_Cohorts_Utxo_Sth(client) self.lth: SeriesTree_Cohorts_Utxo_Lth = SeriesTree_Cohorts_Utxo_Lth(client) @@ -6428,7 +6450,7 @@ class SeriesTree_Cohorts_Utxo: class SeriesTree_Cohorts_Addr_OverAmount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._1sat: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_over_1sat') self._10sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_over_10sats') self._100sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_over_100sats') @@ -6446,7 +6468,7 @@ class SeriesTree_Cohorts_Addr_OverAmount: class SeriesTree_Cohorts_Addr_AmountRange: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._0sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_0sats') self._1sat_to_10sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_1sat_to_10sats') self._10sats_to_100sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_10sats_to_100sats') @@ -6466,7 +6488,7 @@ class SeriesTree_Cohorts_Addr_AmountRange: class SeriesTree_Cohorts_Addr_UnderAmount: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self._10sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_under_10sats') self._100sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_under_100sats') self._1k_sats: ActivityAddrOutputsRealizedSupplyUnrealizedPattern = ActivityAddrOutputsRealizedSupplyUnrealizedPattern(client, 'addrs_under_1k_sats') @@ -6484,7 +6506,7 @@ class SeriesTree_Cohorts_Addr_UnderAmount: class SeriesTree_Cohorts_Addr: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.over_amount: SeriesTree_Cohorts_Addr_OverAmount = SeriesTree_Cohorts_Addr_OverAmount(client) self.amount_range: SeriesTree_Cohorts_Addr_AmountRange = SeriesTree_Cohorts_Addr_AmountRange(client) self.under_amount: SeriesTree_Cohorts_Addr_UnderAmount = SeriesTree_Cohorts_Addr_UnderAmount(client) @@ -6492,14 +6514,14 @@ class SeriesTree_Cohorts_Addr: class SeriesTree_Cohorts: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.utxo: SeriesTree_Cohorts_Utxo = SeriesTree_Cohorts_Utxo(client) self.addr: SeriesTree_Cohorts_Addr = SeriesTree_Cohorts_Addr(client) class SeriesTree: """Series tree node.""" - def __init__(self, client: BrkClientBase, base_path: str = ''): + def __init__(self, client: BrkClient, base_path: str = ''): self.blocks: SeriesTree_Blocks = SeriesTree_Blocks(client) self.transactions: SeriesTree_Transactions = SeriesTree_Transactions(client) self.inputs: SeriesTree_Inputs = SeriesTree_Inputs(client) diff --git a/packages/brk_client/pyproject.toml b/packages/brk_client/pyproject.toml index c88bd321c..151fd3071 100644 --- a/packages/brk_client/pyproject.toml +++ b/packages/brk_client/pyproject.toml @@ -35,5 +35,10 @@ dev = [ requires = ["hatchling"] build-backend = "hatchling.build" +[tool.pytest.ini_options] +# `check_endpoints.py` at the root of tests/mempool_compat needs the extra glob. +python_files = ["test_*.py", "check_*.py"] +testpaths = ["tests"] + [tool.ruff.lint] ignore = ["E701"] diff --git a/packages/brk_client/tests/mempool_compat/addresses/test_address_info.py b/packages/brk_client/tests/mempool_compat/addresses/test_address_info.py new file mode 100644 index 000000000..85899de81 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/addresses/test_address_info.py @@ -0,0 +1,48 @@ +"""GET /api/address/{address}""" + +import pytest + +from _lib import assert_same_structure, show + + +@pytest.fixture(params=[ + "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", # P2PKH — early block reward + "3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", # P2SH +], ids=["p2pkh", "p2sh"]) +def static_addr(request): + """Well-known addresses that always exist.""" + return request.param + + +def test_address_info_static(brk, mempool, static_addr): + """Address stats structure must match for well-known addresses.""" + path = f"/api/address/{static_addr}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) + assert b["address"] == m["address"] + + +def test_address_info_discovered(brk, mempool, live_addrs): + """Address stats structure must match for each discovered type.""" + for atype, addr in live_addrs: + path = f"/api/address/{addr}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{atype}]", b, m) + assert_same_structure(b, m) + assert b["address"] == m["address"] + + +def test_address_chain_stats_close(brk, mempool, live_addrs): + """Chain stats values must be close for each discovered address.""" + for atype, addr in live_addrs: + path = f"/api/address/{addr}" + b = brk.get_json(path)["chain_stats"] + m = mempool.get_json(path)["chain_stats"] + show("GET", f"{path} [chain_stats, {atype}]", b, m) + assert_same_structure(b, m) + assert abs(b["tx_count"] - m["tx_count"]) <= 5, ( + f"{atype} tx_count: brk={b['tx_count']} vs mempool={m['tx_count']}" + ) diff --git a/packages/brk_client/tests/mempool_compat/addresses/test_address_txs.py b/packages/brk_client/tests/mempool_compat/addresses/test_address_txs.py new file mode 100644 index 000000000..f3d4f534a --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/addresses/test_address_txs.py @@ -0,0 +1,49 @@ +"""GET /api/address/{address}/txs""" + +import pytest + +from _lib import assert_same_structure, show + + +@pytest.fixture(params=[ + "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", + "3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", +], ids=["p2pkh", "p2sh"]) +def static_addr(request): + return request.param + + +def test_address_txs_static(brk, mempool, static_addr): + """Confirmed+mempool tx list structure must match for well-known addresses.""" + path = f"/api/address/{static_addr}/txs" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") + assert isinstance(b, list) and isinstance(m, list) + if b and m: + assert_same_structure(b[0], m[0]) + + +def test_address_txs_discovered(brk, mempool, live_addrs): + """Confirmed+mempool tx list structure must match for each discovered type.""" + for atype, addr in live_addrs: + path = f"/api/address/{addr}/txs" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{atype}]", f"({len(b)} txs)", f"({len(m)} txs)") + assert isinstance(b, list) and isinstance(m, list) + if b and m: + assert_same_structure(b[0], m[0]) + + +def test_address_txs_fields(brk, mempool, live): + """Every tx in the list must carry the core mempool.space fields.""" + path = f"/api/address/{live.sample_address}/txs" + b = brk.get_json(path) + show("GET", path, f"({len(b)} txs)", "—") + if not b: + pytest.skip("address has no txs in brk") + required = {"txid", "version", "locktime", "vin", "vout", "size", "weight", "fee", "status"} + for tx in b[:5]: + missing = required - set(tx.keys()) + assert not missing, f"tx {tx.get('txid', '?')} missing fields: {missing}" diff --git a/packages/brk_client/tests/mempool_compat/addresses/test_address_txs_chain.py b/packages/brk_client/tests/mempool_compat/addresses/test_address_txs_chain.py new file mode 100644 index 000000000..2c3d01e27 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/addresses/test_address_txs_chain.py @@ -0,0 +1,49 @@ +"""GET /api/address/{address}/txs/chain""" + +import pytest + +from _lib import assert_same_structure, show + + +@pytest.fixture(params=[ + "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", + "3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", +], ids=["p2pkh", "p2sh"]) +def static_addr(request): + return request.param + + +def test_address_txs_chain_static(brk, mempool, static_addr): + """Confirmed-only tx list structure must match for well-known addresses.""" + path = f"/api/address/{static_addr}/txs/chain" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") + assert isinstance(b, list) and isinstance(m, list) + if b and m: + assert_same_structure(b[0], m[0]) + + +def test_address_txs_chain_discovered(brk, mempool, live_addrs): + """Confirmed-only tx list structure must match for each discovered type.""" + for atype, addr in live_addrs: + path = f"/api/address/{addr}/txs/chain" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{atype}]", f"({len(b)} txs)", f"({len(m)} txs)") + assert isinstance(b, list) and isinstance(m, list) + if b and m: + assert_same_structure(b[0], m[0]) + + +def test_address_txs_chain_all_confirmed(brk, live): + """Every tx returned by /txs/chain must have confirmed=True in its status.""" + path = f"/api/address/{live.sample_address}/txs/chain" + b = brk.get_json(path) + show("GET", path, f"({len(b)} txs)", "—") + if not b: + pytest.skip("address has no confirmed txs in brk") + unconfirmed = [t for t in b if not t.get("status", {}).get("confirmed", False)] + assert not unconfirmed, ( + f"{len(unconfirmed)} unconfirmed tx(s) returned by /txs/chain" + ) diff --git a/packages/brk_client/tests/mempool_compat/addresses/test_address_txs_mempool.py b/packages/brk_client/tests/mempool_compat/addresses/test_address_txs_mempool.py new file mode 100644 index 000000000..5cdf60167 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/addresses/test_address_txs_mempool.py @@ -0,0 +1,33 @@ +"""GET /api/address/{address}/txs/mempool""" + +from _lib import show + + +def test_address_txs_mempool_sample(brk, mempool, live): + """Mempool tx list must be an array (contents are volatile).""" + path = f"/api/address/{live.sample_address}/txs/mempool" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") + assert isinstance(b, list) and isinstance(m, list) + + +def test_address_txs_mempool_discovered(brk, mempool, live_addrs): + """Mempool tx list must be a (possibly empty) array for each discovered type.""" + for atype, addr in live_addrs: + path = f"/api/address/{addr}/txs/mempool" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{atype}]", f"({len(b)} txs)", f"({len(m)} txs)") + assert isinstance(b, list) and isinstance(m, list) + + +def test_address_txs_mempool_all_unconfirmed(brk, live): + """Every tx returned by /txs/mempool must have confirmed=False (if any).""" + path = f"/api/address/{live.sample_address}/txs/mempool" + b = brk.get_json(path) + show("GET", path, f"({len(b)} txs)", "—") + confirmed = [t for t in b if t.get("status", {}).get("confirmed", False)] + assert not confirmed, ( + f"{len(confirmed)} confirmed tx(s) returned by /txs/mempool" + ) diff --git a/packages/brk_client/tests/mempool_compat/addresses/test_address_utxo.py b/packages/brk_client/tests/mempool_compat/addresses/test_address_utxo.py new file mode 100644 index 000000000..514b70266 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/addresses/test_address_utxo.py @@ -0,0 +1,52 @@ +"""GET /api/address/{address}/utxo""" + +import pytest + +from _lib import assert_same_values, show + + +@pytest.fixture(params=[ + "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", + "3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", +], ids=["p2pkh", "p2sh"]) +def static_addr(request): + return request.param + + +def test_address_utxo_static(brk, mempool, static_addr): + """UTXO list must match — same txids, values, and statuses.""" + path = f"/api/address/{static_addr}/utxo" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} utxos)", f"({len(m)} utxos)") + assert isinstance(b, list) and isinstance(m, list) + key = lambda u: (u.get("txid", ""), u.get("vout", 0)) + b_sorted = sorted(b, key=key) + m_sorted = sorted(m, key=key) + assert_same_values(b_sorted, m_sorted) + + +def test_address_utxo_discovered(brk, mempool, live_addrs): + """UTXO list must match for each discovered address type — same txids, values, and statuses.""" + for atype, addr in live_addrs: + path = f"/api/address/{addr}/utxo" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{atype}]", f"({len(b)} utxos)", f"({len(m)} utxos)") + assert isinstance(b, list) and isinstance(m, list) + key = lambda u: (u.get("txid", ""), u.get("vout", 0)) + assert_same_values(sorted(b, key=key), sorted(m, key=key)) + + +def test_address_utxo_fields(brk, live): + """Every utxo must carry the core mempool.space fields.""" + path = f"/api/address/{live.sample_address}/utxo" + b = brk.get_json(path) + show("GET", path, f"({len(b)} utxos)", "—") + if not b: + pytest.skip("address has no utxos in brk") + required = {"txid", "vout", "value", "status"} + for u in b[:5]: + missing = required - set(u.keys()) + assert not missing, f"utxo {u.get('txid', '?')}:{u.get('vout', '?')} missing fields: {missing}" + assert isinstance(u["value"], int) and u["value"] > 0 diff --git a/packages/brk_client/tests/mempool_compat/addresses/test_validate_address.py b/packages/brk_client/tests/mempool_compat/addresses/test_validate_address.py new file mode 100644 index 000000000..71c44de10 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/addresses/test_validate_address.py @@ -0,0 +1,53 @@ +"""GET /api/v1/validate-address/{address}""" + +import pytest + +from _lib import assert_same_structure, assert_same_values, show + + +def test_validate_address_discovered(brk, mempool, live_addrs): + """Validation of each discovered address type must match exactly.""" + for atype, addr in live_addrs: + path = f"/api/v1/validate-address/{addr}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{atype}]", b, m) + assert_same_values(b, m) + assert b["isvalid"] is True + + +@pytest.mark.parametrize("addr,kind", [ + ("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "p2pkh-genesis"), + ("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", "p2sh"), + ("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", "p2wpkh"), + ("bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", "p2tr"), +]) +def test_validate_address_static_valid(brk, mempool, addr, kind): + """Well-known addresses across all script types must validate identically.""" + path = f"/api/v1/validate-address/{addr}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{kind}]", b, m) + assert_same_values(b, m) + assert b["isvalid"] is True + + +@pytest.mark.parametrize("addr,kind", [ + ("notanaddress123", "garbage"), + ("", "empty"), + ("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNb", "bad-checksum-p2pkh"), + ("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", "bad-checksum-p2wpkh"), + ("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLz", "bad-checksum-p2sh"), +]) +def test_validate_address_invalid(brk, mempool, addr, kind): + """Invalid addresses must produce the same rejection structure.""" + path = f"/api/v1/validate-address/{addr}" + if kind == "empty": + # An empty path segment routes to a different endpoint — skip. + pytest.skip("empty address routes to a different endpoint") + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", f"{path} [{kind}]", b, m) + assert b["isvalid"] is False + assert m["isvalid"] is False + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block.py b/packages/brk_client/tests/mempool_compat/blocks/test_block.py new file mode 100644 index 000000000..2a9bd32b4 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block.py @@ -0,0 +1,12 @@ +"""GET /api/block/{hash}""" + +from _lib import assert_same_values, show + + +def test_block_by_hash(brk, mempool, block): + """Confirmed block info must be identical.""" + path = f"/api/block/{block.hash}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_header.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_header.py new file mode 100644 index 000000000..2ba20c5c0 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_header.py @@ -0,0 +1,13 @@ +"""GET /api/block/{hash}/header""" + +from _lib import show + + +def test_block_header(brk, mempool, block): + """80-byte hex block header must be identical.""" + path = f"/api/block/{block.hash}/header" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b, m) + assert len(b) == 160, f"Expected 160 hex chars (80 bytes), got {len(b)}" + assert b == m diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_height.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_height.py new file mode 100644 index 000000000..042e3af91 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_height.py @@ -0,0 +1,13 @@ +"""GET /api/block-height/{height}""" + +from _lib import show + + +def test_block_height_to_hash(brk, mempool, block): + """Block hash at a given height must match.""" + path = f"/api/block-height/{block.height}" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b, m) + assert b == m + assert b == block.hash diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_raw.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_raw.py new file mode 100644 index 000000000..2be8d47d0 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_raw.py @@ -0,0 +1,13 @@ +"""GET /api/block/{hash}/raw""" + +from _lib import show + + +def test_block_raw(brk, mempool, block): + """Raw block bytes must be identical and start with the 80-byte header.""" + path = f"/api/block/{block.hash}/raw" + b = brk.get_bytes(path) + m = mempool.get_bytes(path) + show("GET", path, f"<{len(b)} bytes>", f"<{len(m)} bytes>") + assert b == m + assert len(b) >= 80 diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_status.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_status.py new file mode 100644 index 000000000..d1c96991c --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_status.py @@ -0,0 +1,12 @@ +"""GET /api/block/{hash}/status""" + +from _lib import assert_same_values, show + + +def test_block_status(brk, mempool, block): + """Block status must be identical for a confirmed block.""" + path = f"/api/block/{block.hash}/status" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_txid_index.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_txid_index.py new file mode 100644 index 000000000..c6b92bfc0 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_txid_index.py @@ -0,0 +1,37 @@ +"""GET /api/block/{hash}/txid/{index}""" + +import pytest + +from _lib import show + + +def test_block_txid_at_index_0(brk, mempool, block): + """Txid at position 0 (coinbase) must match.""" + path = f"/api/block/{block.hash}/txid/0" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b, m) + assert b == m + + +def test_block_txid_at_index_1(brk, mempool, block): + """Txid at position 1 (first non-coinbase) must match.""" + txids = mempool.get_json(f"/api/block/{block.hash}/txids") + if len(txids) <= 1: + pytest.skip("block has only coinbase") + path = f"/api/block/{block.hash}/txid/1" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b, m) + assert b == m + + +def test_block_txid_at_last_index(brk, mempool, block): + """Txid at last position must match.""" + txids = mempool.get_json(f"/api/block/{block.hash}/txids") + last = len(txids) - 1 + path = f"/api/block/{block.hash}/txid/{last}" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b, m) + assert b == m diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_txids.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_txids.py new file mode 100644 index 000000000..e8c3adc01 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_txids.py @@ -0,0 +1,12 @@ +"""GET /api/block/{hash}/txids""" + +from _lib import show + + +def test_block_txids(brk, mempool, block): + """Ordered txid list must be identical.""" + path = f"/api/block/{block.hash}/txids" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b[:3], m[:3]) + assert b == m diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_txs.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_txs.py new file mode 100644 index 000000000..84df51676 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_txs.py @@ -0,0 +1,14 @@ +"""GET /api/block/{hash}/txs""" + +from _lib import assert_same_values, show + + +def test_block_txs_page0(brk, mempool, block): + """First page of block transactions must match.""" + path = f"/api/block/{block.hash}/txs" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") + assert len(b) == len(m), f"Page size: brk={len(b)} vs mempool={len(m)}" + if b and m: + assert_same_values(b[0], m[0], exclude={"sigops"}) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_txs_start.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_txs_start.py new file mode 100644 index 000000000..cce10f587 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_txs_start.py @@ -0,0 +1,68 @@ +"""GET /api/block/{hash}/txs/{start_index}""" + +import pytest + +from _lib import assert_same_structure, show + + +def test_block_txs_start_index_25(brk, mempool, block): + """Paginated txs from index 25 must match (skip small blocks).""" + txids = mempool.get_json(f"/api/block/{block.hash}/txids") + if len(txids) <= 25: + pytest.skip(f"block has only {len(txids)} txs") + path = f"/api/block/{block.hash}/txs/25" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") + assert len(b) == len(m) + if b and m: + assert_same_structure(b[0], m[0]) + + +def test_block_txs_start_index_zero(brk, mempool, block): + """`/txs/0` must mirror `/txs` (the default page) in length and structure.""" + path0 = f"/api/block/{block.hash}/txs/0" + pathx = f"/api/block/{block.hash}/txs" + b0 = brk.get_json(path0) + bx = brk.get_json(pathx) + show("GET", path0, f"({len(b0)} txs)", f"vs /txs ({len(bx)} txs)") + assert len(b0) == len(bx) + if b0 and bx: + assert b0[0]["txid"] == bx[0]["txid"] + + +def test_block_txs_start_aligned_pagination(brk, mempool, block): + """Pages at 0, 25, 50 must each be aligned slices of the full txid list.""" + txids = mempool.get_json(f"/api/block/{block.hash}/txids") + if len(txids) <= 50: + pytest.skip(f"block has only {len(txids)} txs") + # mempool.space orders txids tip-first inside the block payload, but + # /txids returns them in block order (coinbase-first). Paged /txs follows + # the same coinbase-first order — so page N starts at offset N. + page0 = brk.get_json(f"/api/block/{block.hash}/txs/0") + page25 = brk.get_json(f"/api/block/{block.hash}/txs/25") + page50 = brk.get_json(f"/api/block/{block.hash}/txs/50") + show("GET", f"/api/block/{block.hash}/txs/{{0,25,50}}", + f"page0={len(page0)} page25={len(page25)} page50={len(page50)}", "—") + # The paging origin is what mempool.space does; verify against the live + # /txids list rather than re-deriving the order ourselves. + assert page0 and page0[0]["txid"] == txids[0] + assert page25 and page25[0]["txid"] == txids[25] + assert page50 and page50[0]["txid"] == txids[50] + + +def test_block_txs_start_past_end(brk, mempool, block): + """A start index past the last tx must produce the same response on both servers.""" + txids = mempool.get_json(f"/api/block/{block.hash}/txids") + past = len(txids) + 1000 + path = f"/api/block/{block.hash}/txs/{past}" + b_resp = brk.get_raw(path) + m_resp = mempool.get_raw(path) + show("GET", path, f"brk={b_resp.status_code}", f"mempool={m_resp.status_code}") + assert b_resp.status_code == m_resp.status_code, ( + f"past-end status differs: brk={b_resp.status_code} vs mempool={m_resp.status_code}" + ) + if b_resp.status_code == 200: + assert b_resp.json() == m_resp.json(), ( + f"past-end body differs: brk={b_resp.json()} vs mempool={m_resp.json()}" + ) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_block_v1.py b/packages/brk_client/tests/mempool_compat/blocks/test_block_v1.py new file mode 100644 index 000000000..97523c402 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_block_v1.py @@ -0,0 +1,22 @@ +"""GET /api/v1/block/{hash}""" + +from _lib import assert_same_structure, assert_same_values, show + + +def test_block_v1_extras_all_values(brk, mempool, block): + """Every shared extras field must match — exposes computation differences.""" + path = f"/api/v1/block/{block.hash}" + b = brk.get_json(path)["extras"] + m = mempool.get_json(path)["extras"] + show("GET", f"{path} [extras]", b, m, max_lines=50) + assert_same_structure(b, m) + assert_same_values(b, m) + + +def test_block_v1_extras_pool(brk, mempool, block): + """Pool identification structure must match.""" + path = f"/api/v1/block/{block.hash}" + bp = brk.get_json(path)["extras"]["pool"] + mp = mempool.get_json(path)["extras"]["pool"] + show("GET", f"{path} [extras.pool]", bp, mp) + assert_same_structure(bp, mp) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_blocks_height.py b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_height.py new file mode 100644 index 000000000..ba85ebf2c --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_height.py @@ -0,0 +1,14 @@ +"""GET /api/blocks/{height}""" + +from _lib import assert_same_values, show + + +def test_blocks_from_height(brk, mempool, block): + """Confirmed blocks from a fixed height must match exactly.""" + path = f"/api/blocks/{block.height}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") + assert len(b) == len(m) + if b and m: + assert_same_values(b[0], m[0]) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_blocks_recent.py b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_recent.py new file mode 100644 index 000000000..294611233 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_recent.py @@ -0,0 +1,35 @@ +"""GET /api/blocks (most recent confirmed blocks, no height)""" + +from _lib import assert_same_structure, show + + +def test_blocks_recent_structure(brk, mempool): + """Recent blocks list must have the same element structure.""" + path = "/api/blocks" + b = brk.get_json(path) + m = mempool.get_json(path) + show( + "GET", path, + f"({len(b)} blocks, {b[-1]['height']}-{b[0]['height']})" if b else "[]", + f"({len(m)} blocks, {m[-1]['height']}-{m[0]['height']})" if m else "[]", + ) + assert len(b) > 0 + assert_same_structure(b, m) + + +def test_blocks_recent_ordering(brk): + """Returned blocks must be ordered tip-first by strictly decreasing height.""" + b = brk.get_json("/api/blocks") + heights = [blk["height"] for blk in b] + show("GET", "/api/blocks", f"heights={heights[:5]}...", "—") + assert heights == sorted(heights, reverse=True), ( + f"blocks are not strictly tip-first: {heights}" + ) + assert len(set(heights)) == len(heights), "duplicate heights in /api/blocks" + + +def test_blocks_recent_count(brk): + """mempool.space returns up to 15 blocks; brk should match that contract.""" + b = brk.get_json("/api/blocks") + show("GET", "/api/blocks", f"({len(b)} blocks)", "—") + assert 1 <= len(b) <= 15, f"unexpected block count: {len(b)}" diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_hash.py b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_hash.py new file mode 100644 index 000000000..47b6092b7 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_hash.py @@ -0,0 +1,37 @@ +"""GET /api/blocks/tip/hash""" + +from _lib import show + + +def test_blocks_tip_hash_format(brk, mempool): + """Tip hash must be a valid 64-char hex string on both servers.""" + path = "/api/blocks/tip/hash" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b, m) + assert len(b) == 64 and all(c in "0123456789abcdef" for c in b.lower()) + assert len(m) == 64 and all(c in "0123456789abcdef" for c in m.lower()) + + +def test_blocks_tip_hash_matches_height(brk): + """`tip/hash` must equal `block-height/{tip_height}`.""" + h = int(brk.get_text("/api/blocks/tip/height")) + by_height = brk.get_text(f"/api/block-height/{h}") + tip_hash = brk.get_text("/api/blocks/tip/hash") + show("GET", "/api/blocks/tip/hash", tip_hash, by_height) + # Allow a one-block race if a new block landed between the two fetches. + if tip_hash != by_height: + h2 = int(brk.get_text("/api/blocks/tip/height")) + assert h2 != h or tip_hash == by_height, ( + f"tip/hash={tip_hash} but block-height/{h}={by_height}" + ) + + +def test_blocks_tip_hash_matches_recent(brk): + """`tip/hash` must equal the first hash in `/api/blocks`.""" + tip_hash = brk.get_text("/api/blocks/tip/hash") + blocks = brk.get_json("/api/blocks") + show("GET", "/api/blocks/tip/hash", tip_hash, blocks[0]["id"]) + assert blocks and blocks[0]["id"] == tip_hash, ( + f"tip/hash={tip_hash} but /api/blocks[0].id={blocks[0].get('id')}" + ) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_height.py b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_height.py new file mode 100644 index 000000000..fac47bcff --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_tip_height.py @@ -0,0 +1,32 @@ +"""GET /api/blocks/tip/height""" + +from _lib import show + + +def test_blocks_tip_height_close(brk, mempool): + """Tip heights must be within a few blocks of each other.""" + path = "/api/blocks/tip/height" + b = int(brk.get_text(path)) + m = int(mempool.get_text(path)) + show("GET", path, b, m) + assert abs(b - m) <= 3, f"Tip heights differ by {abs(b - m)}: brk={b}, mempool={m}" + + +def test_blocks_tip_height_resolves_to_hash(brk): + """`tip/height` must resolve to a valid hash via `block-height/{tip}`.""" + h = int(brk.get_text("/api/blocks/tip/height")) + bh = brk.get_text(f"/api/block-height/{h}") + show("GET", "/api/blocks/tip/height", h, bh) + assert len(bh) == 64 and all(c in "0123456789abcdef" for c in bh.lower()), ( + f"block-height/{h} returned non-hash: {bh!r}" + ) + + +def test_blocks_tip_height_matches_recent(brk): + """`tip/height` must equal the first element's height in `/api/blocks`.""" + h = int(brk.get_text("/api/blocks/tip/height")) + blocks = brk.get_json("/api/blocks") + show("GET", "/api/blocks/tip/height", h, blocks[0]["height"]) + assert blocks and blocks[0]["height"] == h, ( + f"tip/height={h} but /api/blocks[0].height={blocks[0]['height']}" + ) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_height.py b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_height.py new file mode 100644 index 000000000..b15721457 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_height.py @@ -0,0 +1,14 @@ +"""GET /api/v1/blocks/{height}""" + +from _lib import assert_same_values, show + + +def test_blocks_v1_from_height(brk, mempool, block): + """v1 blocks from a confirmed height — all values must match.""" + path = f"/api/v1/blocks/{block.height}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") + assert len(b) == len(m) + if b and m: + assert_same_values(b[0], m[0]) diff --git a/packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_recent.py b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_recent.py new file mode 100644 index 000000000..764a3f042 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/blocks/test_blocks_v1_recent.py @@ -0,0 +1,31 @@ +"""GET /api/v1/blocks (with extras, no height)""" + +from _lib import assert_same_structure, show + + +def test_blocks_v1_recent_structure(brk, mempool): + """Recent v1 blocks (with extras) must have the same structure.""" + path = "/api/v1/blocks" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") + assert len(b) > 0 + assert_same_structure(b, m) + + +def test_blocks_v1_recent_ordering(brk): + """v1 blocks must also be tip-first.""" + b = brk.get_json("/api/v1/blocks") + heights = [blk["height"] for blk in b] + show("GET", "/api/v1/blocks", f"heights={heights[:5]}...", "—") + assert heights == sorted(heights, reverse=True), ( + f"v1 blocks are not strictly tip-first: {heights}" + ) + + +def test_blocks_v1_recent_has_extras(brk): + """Each v1 block must carry the extras envelope (v1 distinguishes itself from /api/blocks).""" + b = brk.get_json("/api/v1/blocks") + show("GET", "/api/v1/blocks", f"({len(b)} blocks)", "—") + assert b + assert "extras" in b[0], f"v1 blocks element missing 'extras': {list(b[0].keys())}" diff --git a/packages/brk_client/tests/mempool_compat/check_endpoints.py b/packages/brk_client/tests/mempool_compat/check_endpoints.py new file mode 100644 index 000000000..d65f970db --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/check_endpoints.py @@ -0,0 +1,158 @@ +""" +Global registry checks for the mempool.space compatibility suite. + +These tests don't poke individual endpoints — they verify the *set* of +endpoints brk exposes matches the registry in `_endpoints.py`. If +mempool.space adds a new endpoint, classify it as covered or skipped here so +this file fails loudly on the next CI run. + +Checks: + 1. Every `covered` endpoint actually appears in brk's live `/openapi.json`. + 2. Every `covered` endpoint has a test file at its declared `test_file` path. + 3. Every `skipped` endpoint is NOT exposed by brk (proves the skip is real). + 4. Every brk path that *looks* like a mempool path is classified — no + orphan routes that we silently added without registering. + 5. Brk extensions listed in `BRK_EXTENSIONS` actually exist on brk. +""" + +from pathlib import Path + +import pytest + +from _endpoints import ( + BRK_EXTENSIONS, + MEMPOOL_ENDPOINTS, + Endpoint, + covered_endpoints, + skipped_endpoints, +) + + +HERE = Path(__file__).parent + + +# ---- Brk-side discovery ------------------------------------------------- + + +_HTTP_METHODS = {"get", "post", "put", "delete", "patch", "head", "options"} + + +@pytest.fixture(scope="module") +def brk_routes(brk) -> set[tuple[str, str]]: + """Every `(METHOD, /api/...)` pair brk reports in its OpenAPI spec.""" + spec = brk.get_json("/openapi.json") + return { + (method.upper(), path) + for path, ops in spec["paths"].items() + if path.startswith("/api") + for method in ops.keys() + if method.lower() in _HTTP_METHODS + } + + +@pytest.fixture(scope="module") +def brk_paths(brk_routes) -> set[str]: + """Just the path strings (collapsed across methods).""" + return {path for _, path in brk_routes} + + +@pytest.fixture(scope="module") +def brk_compat_paths(brk_paths) -> set[str]: + """Brk paths that are part of the mempool.space compat surface. + + Strips out brk-only namespaces (series, metrics, urpd, vecs, server, etc.) + so we're left with paths that belong in the registry. + """ + brk_only_prefixes = ( + "/api/series", + "/api/metric", + "/api/metrics", + "/api/urpd", + "/api/vecs", + "/api/server", + "/api.json", + ) + return {p for p in brk_paths if not p.startswith(brk_only_prefixes)} + + +# ---- Checks ------------------------------------------------------------- + + +@pytest.mark.parametrize("endpoint", covered_endpoints(), ids=lambda e: e.path) +def test_covered_endpoint_exposed_by_brk(brk_routes, endpoint: Endpoint): + """Every covered endpoint must appear in brk's OpenAPI under the same method.""" + assert (endpoint.method, endpoint.path) in brk_routes, ( + f"{endpoint.method} {endpoint.path} is marked covered in _endpoints.py " + f"but brk's /openapi.json doesn't expose it" + ) + + +@pytest.mark.parametrize("endpoint", covered_endpoints(), ids=lambda e: e.path) +def test_covered_endpoint_has_test_file(endpoint: Endpoint): + """Every covered endpoint must have a test file at its declared path.""" + assert endpoint.test_file is not None, ( + f"{endpoint.path} is covered but has no test_file" + ) + file = HERE / endpoint.test_file + assert file.is_file(), ( + f"{endpoint.path} declares test_file={endpoint.test_file!r}, " + f"but {file} doesn't exist" + ) + + +@pytest.mark.parametrize("endpoint", skipped_endpoints(), ids=lambda e: e.path) +def test_skipped_endpoint_not_exposed_by_brk(brk_routes, endpoint: Endpoint): + """Every skipped endpoint must be absent from brk's OpenAPI for that method.""" + assert (endpoint.method, endpoint.path) not in brk_routes, ( + f"{endpoint.method} {endpoint.path} is marked skipped " + f"({endpoint.skip_reason!r}) but brk now exposes it — please update " + f"_endpoints.py to mark it covered and add a test" + ) + + +def test_no_orphan_brk_routes(brk_compat_paths): + """Every brk compat path must be classified in the registry. + + If this fails, brk has a route that looks like a mempool.space endpoint + but isn't tracked. Either add it to MEMPOOL_ENDPOINTS (covered + a test) + or to BRK_EXTENSIONS (brk-only with a one-line justification in source). + """ + registry_paths = {e.path for e in MEMPOOL_ENDPOINTS} + extension_paths = set(BRK_EXTENSIONS) + known = registry_paths | extension_paths + orphans = brk_compat_paths - known + assert not orphans, ( + f"Brk exposes {len(orphans)} unclassified mempool-style routes:\n " + + "\n ".join(sorted(orphans)) + + "\nClassify each in mempool_compat/_endpoints.py." + ) + + +@pytest.mark.parametrize("path", BRK_EXTENSIONS, ids=lambda p: p) +def test_brk_extension_actually_exists(brk_paths, path: str): + """Each path in BRK_EXTENSIONS must exist in brk's OpenAPI. + + Stale entries get caught here so the list stays accurate. + """ + assert path in brk_paths, ( + f"{path} is listed in BRK_EXTENSIONS but brk's /openapi.json doesn't " + f"expose it — remove it from _endpoints.py" + ) + + +def test_registry_has_no_duplicates(): + """Each (method, path) pair appears at most once in MEMPOOL_ENDPOINTS.""" + seen: set[tuple[str, str]] = set() + dups: list[tuple[str, str]] = [] + for e in MEMPOOL_ENDPOINTS: + key = (e.method, e.path) + if key in seen: + dups.append(key) + seen.add(key) + assert not dups, f"Duplicate registry entries: {dups}" + + +def test_skipped_endpoints_have_reason(): + """Every skipped endpoint must include a skip_reason.""" + bad = [e for e in skipped_endpoints() if not e.skip_reason] + assert not bad, f"Skipped endpoints missing skip_reason: {[e.path for e in bad]}" diff --git a/packages/brk_client/tests/mempool_compat/conftest.py b/packages/brk_client/tests/mempool_compat/conftest.py new file mode 100644 index 000000000..e046b313c --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/conftest.py @@ -0,0 +1,216 @@ +""" +Shared fixtures for mempool.space compatibility tests. + +Helper functions live in `_lib.py`; this file holds only fixtures so pytest +can discover them throughout the subtree. Each subtree test imports helpers +with `from _lib import ...` — the conftest puts this directory on sys.path. + +Usage: + cd packages/brk_client + uv run pytest tests/mempool_compat -sv # all + uv run pytest tests/mempool_compat/blocks -sv # one category + uv run pytest tests/mempool_compat/blocks/test_block.py -sv # one endpoint + BRK_URL=http://host:port uv run pytest tests/mempool_compat -sv # custom server + +Environment variables: + BRK_URL brk server base URL (default: http://localhost:3110) + MEMPOOL_URL mempool.space base URL (default: https://mempool.space) + RATE_LIMIT seconds between mempool.space requests (default: 0.5) +""" + +import os +import sys +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +import pytest +import requests + +# Make `_lib` and `_endpoints` importable from any nested test file. +sys.path.insert(0, str(Path(__file__).parent)) + +BRK_BASE = os.environ.get("BRK_URL", "http://localhost:3110") +MEMPOOL_BASE = os.environ.get("MEMPOOL_URL", "https://mempool.space") +RATE_LIMIT = float(os.environ.get("RATE_LIMIT", "0.5")) + + +class ApiClient: + """HTTP client for a single API server with optional rate limiting.""" + + def __init__(self, base_url: str, name: str, rate_limit: float = 0.0): + self.base_url = base_url.rstrip("/") + self.name = name + self.rate_limit = rate_limit + self._last_request = 0.0 + self.session = requests.Session() + self.session.headers["User-Agent"] = "brk-compat-test/1.0" + + def _wait(self): + if self.rate_limit > 0: + elapsed = time.monotonic() - self._last_request + if elapsed < self.rate_limit: + time.sleep(self.rate_limit - elapsed) + self._last_request = time.monotonic() + + def get(self, path: str, params=None, timeout: int = 30) -> requests.Response: + self._wait() + url = f"{self.base_url}{path}" + for _ in range(3): + resp = self.session.get(url, params=params, timeout=timeout) + if resp.status_code == 429: + wait = int(resp.headers.get("Retry-After", 5)) + time.sleep(wait) + continue + resp.raise_for_status() + return resp + resp.raise_for_status() + return resp + + def get_raw(self, path: str, params=None, timeout: int = 30) -> requests.Response: + """Like `get` but does not raise on non-2xx — returns the raw response.""" + self._wait() + url = f"{self.base_url}{path}" + return self.session.get(url, params=params, timeout=timeout) + + def get_json(self, path: str, params=None, timeout: int = 30) -> Any: + return self.get(path, params=params, timeout=timeout).json() + + def get_text(self, path: str, params=None, timeout: int = 30) -> str: + return self.get(path, params=params, timeout=timeout).text + + def get_bytes(self, path: str, params=None, timeout: int = 30) -> bytes: + return self.get(path, params=params, timeout=timeout).content + + +# Absolute heights for well-known eras + relative depths for recent blocks. +# Covers: genesis-era, early, mid, post-halving, taproot-era, recent, near-tip. +FIXED_HEIGHTS = [100, 100_000, 400_000, 630_000, 800_000] +RELATIVE_DEPTHS = [1000, 100, 10] + + +@dataclass +class BlockData: + """A discovered block with associated txids.""" + + height: int + hash: str + txid: str + coinbase_txid: str + + +@dataclass +class LiveData: + """Live blockchain data discovered at session start.""" + + tip_height: int + tip_hash: str + blocks: list # list[BlockData] — multiple depths for parametrized tests + addresses: dict # dict[str, str] — keyed by scriptpubkey_type + stable_height: int + stable_hash: str + stable_block: dict + sample_txid: str + coinbase_txid: str + sample_address: str + + +@pytest.fixture(scope="session") +def brk(): + return ApiClient(BRK_BASE, "brk") + + +@pytest.fixture(scope="session") +def mempool(): + return ApiClient(MEMPOOL_BASE, "mempool.space", rate_limit=RATE_LIMIT) + + +@pytest.fixture(scope="session", autouse=True) +def check_servers(brk, mempool): + """Fail fast if either server is unreachable.""" + try: + brk.get("/api/blocks/tip/height") + except Exception as e: + pytest.exit(f"brk server not reachable at {brk.base_url}: {e}") + try: + mempool.get("/api/blocks/tip/height") + except Exception as e: + pytest.exit(f"mempool.space not reachable at {mempool.base_url}: {e}") + + +@pytest.fixture(scope="session") +def live(mempool) -> LiveData: + """Discover live blockchain data once per session. + + Picks blocks at multiple depths and extracts addresses of different + scriptpubkey types so parametrized tests cover varied real data. + """ + tip_height = int(mempool.get_text("/api/blocks/tip/height")) + tip_hash = mempool.get_text("/api/blocks/tip/hash") + + heights = FIXED_HEIGHTS + [tip_height - d for d in RELATIVE_DEPTHS] + heights.sort() + + blocks: list[BlockData] = [] + addresses: dict[str, str] = {} + + for h in heights: + bh = mempool.get_text(f"/api/block-height/{h}") + txids = mempool.get_json(f"/api/block/{bh}/txids") + coinbase = txids[0] + sample = txids[min(1, len(txids) - 1)] + blocks.append(BlockData(height=h, hash=bh, txid=sample, coinbase_txid=coinbase)) + + if len(addresses) < 8: + tx = mempool.get_json(f"/api/tx/{sample}") + for vout in tx.get("vout", []): + atype = vout.get("scriptpubkey_type") + addr = vout.get("scriptpubkey_address") + if addr and atype and atype not in addresses: + addresses[atype] = addr + + stable = blocks[0] + stable_block = mempool.get_json(f"/api/block/{stable.hash}") + sample_address = next(iter(addresses.values()), "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa") + + data = LiveData( + tip_height=tip_height, + tip_hash=tip_hash, + blocks=blocks, + addresses=addresses, + stable_height=stable.height, + stable_hash=stable.hash, + stable_block=stable_block, + sample_txid=stable.txid, + coinbase_txid=stable.coinbase_txid, + sample_address=sample_address, + ) + + print(f"\n{'=' * 70}") + print(f" LIVE TEST DATA (from {MEMPOOL_BASE})") + print(f"{'=' * 70}") + print(f" tip {data.tip_height} {data.tip_hash[:20]}...") + for i, b in enumerate(blocks): + print(f" block[{i}] {b.height} {b.hash[:20]}... tx={b.txid[:16]}...") + for atype, addr in addresses.items(): + print(f" addr {atype:12s} {addr}") + print(f"{'=' * 70}\n") + return data + + +@pytest.fixture(params=range(8), ids=[ + "h100", "h100k", "h400k", "h630k", "h800k", "recent1k", "recent100", "recent10", +]) +def block(request, live): + """One BlockData per id — skip if not discovered for this session.""" + i = request.param + if i >= len(live.blocks): + pytest.skip("block not discovered") + return live.blocks[i] + + +@pytest.fixture() +def live_addrs(live): + """All dynamically discovered addresses, keyed by scriptpubkey_type.""" + return list(live.addresses.items()) diff --git a/packages/brk_client/tests/mempool_compat/fees/test_mempool_blocks.py b/packages/brk_client/tests/mempool_compat/fees/test_mempool_blocks.py new file mode 100644 index 000000000..d70db0ed0 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/fees/test_mempool_blocks.py @@ -0,0 +1,27 @@ +"""GET /api/v1/fees/mempool-blocks""" + +from _lib import assert_same_structure, show + + +def test_fees_mempool_blocks(brk, mempool): + """Projected mempool blocks must have the same element structure.""" + path = "/api/v1/fees/mempool-blocks" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") + assert isinstance(b, list) and isinstance(m, list) + assert len(b) > 0 + if b and m: + assert_same_structure(b[0], m[0]) + + +def test_fees_mempool_blocks_fee_range(brk, mempool): + """Each projected block must have a 7-element feeRange.""" + path = "/api/v1/fees/mempool-blocks" + for label, client in [("brk", brk), ("mempool", mempool)]: + blocks = client.get_json(path) + for i, block in enumerate(blocks[:3]): + assert "feeRange" in block, f"{label} block {i} missing feeRange" + assert len(block["feeRange"]) == 7, ( + f"{label} block {i} feeRange has {len(block['feeRange'])} items, expected 7" + ) diff --git a/packages/brk_client/tests/mempool_compat/fees/test_precise.py b/packages/brk_client/tests/mempool_compat/fees/test_precise.py new file mode 100644 index 000000000..bfa7815da --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/fees/test_precise.py @@ -0,0 +1,42 @@ +"""GET /api/v1/fees/precise""" + +from _lib import assert_same_structure, show + + +EXPECTED_FEE_KEYS = [ + "fastestFee", "halfHourFee", "hourFee", "economyFee", "minimumFee", +] + + +def test_fees_precise_structure(brk, mempool): + """Precise fees must have the same structure as recommended.""" + path = "/api/v1/fees/precise" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) + for key in EXPECTED_FEE_KEYS: + assert key in b + + +def test_fees_precise_ordering(brk, mempool): + """Precise fee tiers must be ordered: fastest >= halfHour >= hour >= economy >= minimum.""" + path = "/api/v1/fees/precise" + for label, client in [("brk", brk), ("mempool", mempool)]: + d = client.get_json(path) + assert d["fastestFee"] >= d["halfHourFee"] >= d["hourFee"], ( + f"{label}: precise fee ordering violated {d}" + ) + assert d["hourFee"] >= d["economyFee"] >= d["minimumFee"], ( + f"{label}: precise fee ordering violated {d}" + ) + + +def test_fees_precise_numeric(brk): + """Each tier in /precise must be a non-negative number.""" + d = brk.get_json("/api/v1/fees/precise") + show("GET", "/api/v1/fees/precise", d, "—") + for key in EXPECTED_FEE_KEYS: + v = d[key] + assert isinstance(v, (int, float)), f"{key} not numeric: {type(v).__name__}" + assert v >= 0, f"{key} is negative: {v}" diff --git a/packages/brk_client/tests/mempool_compat/fees/test_recommended.py b/packages/brk_client/tests/mempool_compat/fees/test_recommended.py new file mode 100644 index 000000000..0f08ea17c --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/fees/test_recommended.py @@ -0,0 +1,33 @@ +"""GET /api/v1/fees/recommended""" + +from _lib import assert_same_structure, show + + +EXPECTED_FEE_KEYS = [ + "fastestFee", "halfHourFee", "hourFee", "economyFee", "minimumFee", +] + + +def test_fees_recommended(brk, mempool): + """Recommended fees must have the same keys and numeric types.""" + path = "/api/v1/fees/recommended" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) + for key in EXPECTED_FEE_KEYS: + assert key in b, f"brk missing '{key}'" + assert isinstance(b[key], (int, float)), f"'{key}' is not numeric: {type(b[key])}" + + +def test_fees_recommended_ordering(brk, mempool): + """Fee tiers must be ordered: fastest >= halfHour >= hour >= economy >= minimum.""" + path = "/api/v1/fees/recommended" + for label, client in [("brk", brk), ("mempool", mempool)]: + d = client.get_json(path) + assert d["fastestFee"] >= d["halfHourFee"] >= d["hourFee"], ( + f"{label}: fee ordering violated {d}" + ) + assert d["hourFee"] >= d["economyFee"] >= d["minimumFee"], ( + f"{label}: fee ordering violated {d}" + ) diff --git a/packages/brk_client/tests/mempool_compat/general/test_difficulty_adjustment.py b/packages/brk_client/tests/mempool_compat/general/test_difficulty_adjustment.py new file mode 100644 index 000000000..b1d1c59bf --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/general/test_difficulty_adjustment.py @@ -0,0 +1,35 @@ +"""GET /api/v1/difficulty-adjustment""" + +from _lib import assert_same_structure, show + + +DIFFICULTY_KEYS = [ + "progressPercent", "difficultyChange", "estimatedRetargetDate", + "remainingBlocks", "remainingTime", "previousRetarget", + "previousTime", "nextRetargetHeight", "timeAvg", + "adjustedTimeAvg", "timeOffset", "expectedBlocks", +] + + +def test_difficulty_adjustment(brk, mempool): + """Difficulty adjustment must have the same structure.""" + path = "/api/v1/difficulty-adjustment" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) + for key in DIFFICULTY_KEYS: + assert key in b, f"brk missing '{key}'" + + +def test_difficulty_adjustment_values_sane(brk, mempool): + """Progress must be 0-100 %, remaining blocks must be 0-2016.""" + path = "/api/v1/difficulty-adjustment" + for label, client in [("brk", brk), ("mempool", mempool)]: + d = client.get_json(path) + assert 0 <= d["progressPercent"] <= 100, ( + f"{label} progressPercent out of range: {d['progressPercent']}" + ) + assert 0 <= d["remainingBlocks"] <= 2016, ( + f"{label} remainingBlocks out of range: {d['remainingBlocks']}" + ) diff --git a/packages/brk_client/tests/mempool_compat/general/test_historical_price.py b/packages/brk_client/tests/mempool_compat/general/test_historical_price.py new file mode 100644 index 000000000..4e5652288 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/general/test_historical_price.py @@ -0,0 +1,54 @@ +"""GET /api/v1/historical-price (with and without timestamp)""" + +import pytest + +from _lib import assert_same_structure, show + + +# Well-known timestamps from different eras +HISTORICAL_TIMESTAMPS = [ + 1231006505, # genesis block (2009-01-03) + 1354116278, # block 210000 — first halving (2012-11-28) + 1468082773, # block 420000 — second halving (2016-07-09) + 1588788036, # block 630000 — third halving (2020-05-11) + 1713571767, # block 840000 — fourth halving (2024-04-20) +] + + +def test_historical_price(brk, mempool): + """Historical price must have the same structure.""" + path = "/api/v1/historical-price" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m, max_lines=15) + assert_same_structure(b, m) + assert "prices" in b + assert isinstance(b["prices"], list) + + +def test_historical_price_at_block_timestamps(brk, mempool, live): + """Historical price at each discovered block's timestamp must match structure.""" + for block in live.blocks: + info = brk.get_json(f"/api/block/{block.hash}") + ts = info["timestamp"] + path = f"/api/v1/historical-price?timestamp={ts}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) + assert "prices" in b + assert len(b["prices"]) > 0 + + +@pytest.mark.parametrize("ts", HISTORICAL_TIMESTAMPS, ids=[ + "genesis", "halving1", "halving2", "halving3", "halving4", +]) +def test_historical_price_at_era(brk, mempool, ts): + """Historical price at well-known timestamps must match structure.""" + path = f"/api/v1/historical-price?timestamp={ts}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) + assert "prices" in b + assert len(b["prices"]) > 0 diff --git a/packages/brk_client/tests/mempool_compat/general/test_prices.py b/packages/brk_client/tests/mempool_compat/general/test_prices.py new file mode 100644 index 000000000..54953524a --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/general/test_prices.py @@ -0,0 +1,22 @@ +"""GET /api/v1/prices""" + +from _lib import assert_same_structure, show + + +def test_prices(brk, mempool): + """Current price must have the same structure.""" + path = "/api/v1/prices" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) + assert "USD" in b + assert "time" in b + + +def test_prices_positive(brk, mempool): + """USD price must be a positive number on both servers.""" + path = "/api/v1/prices" + for label, client in [("brk", brk), ("mempool", mempool)]: + d = client.get_json(path) + assert d["USD"] > 0, f"{label} USD price is not positive: {d['USD']}" diff --git a/packages/brk_client/tests/mempool_compat/mempool/test_mempool.py b/packages/brk_client/tests/mempool_compat/mempool/test_mempool.py new file mode 100644 index 000000000..d7881ce33 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mempool/test_mempool.py @@ -0,0 +1,23 @@ +"""GET /api/mempool""" + +from _lib import assert_same_structure, show + + +def test_mempool_info(brk, mempool): + """Mempool stats must have the same keys and types.""" + path = "/api/mempool" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m, max_lines=15) + assert_same_structure(b, m) + assert isinstance(b["count"], int) + assert isinstance(b["vsize"], int) + + +def test_mempool_info_positive(brk, mempool): + """Both servers must report a non-empty mempool.""" + path = "/api/mempool" + for label, client in [("brk", brk), ("mempool", mempool)]: + d = client.get_json(path) + assert d["count"] > 0, f"{label} mempool count is 0" + assert d["vsize"] > 0, f"{label} mempool vsize is 0" diff --git a/packages/brk_client/tests/mempool_compat/mempool/test_recent.py b/packages/brk_client/tests/mempool_compat/mempool/test_recent.py new file mode 100644 index 000000000..2c9184a7a --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mempool/test_recent.py @@ -0,0 +1,25 @@ +"""GET /api/mempool/recent""" + +from _lib import assert_same_structure, show + + +def test_mempool_recent(brk, mempool): + """Recent mempool txs must have the same element structure.""" + path = "/api/mempool/recent" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert isinstance(b, list) and isinstance(m, list) + assert len(b) > 0 + if b and m: + assert_same_structure(b[0], m[0]) + + +def test_mempool_recent_fields(brk, mempool): + """Each recent tx must have txid, fee, vsize, value.""" + path = "/api/mempool/recent" + for label, client in [("brk", brk), ("mempool", mempool)]: + txs = client.get_json(path) + for tx in txs[:3]: + for key in ["txid", "fee", "vsize", "value"]: + assert key in tx, f"{label} recent tx missing '{key}': {tx}" diff --git a/packages/brk_client/tests/mempool_compat/mempool/test_txids.py b/packages/brk_client/tests/mempool_compat/mempool/test_txids.py new file mode 100644 index 000000000..8afda7afb --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mempool/test_txids.py @@ -0,0 +1,46 @@ +"""GET /api/mempool/txids""" + +from _lib import show + + +HEX = set("0123456789abcdef") + + +def test_mempool_txids_basic(brk, mempool): + """Txid list must be a non-empty array of strings on both servers.""" + path = "/api/mempool/txids" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} txids)", f"({len(m)} txids)") + assert isinstance(b, list) and isinstance(m, list) + assert len(b) > 0, "brk mempool has no txids" + assert isinstance(b[0], str) and len(b[0]) == 64 + + +def test_mempool_txids_format(brk): + """Every txid in brk's mempool list must be a 64-char lowercase hex string.""" + b = brk.get_json("/api/mempool/txids") + show("GET", "/api/mempool/txids", f"({len(b)} txids)", "—") + bad = [t for t in b if not (isinstance(t, str) and len(t) == 64 and set(t.lower()) <= HEX)] + assert not bad, f"{len(bad)} malformed txid(s), e.g. {bad[0] if bad else None!r}" + + +def test_mempool_txids_unique(brk): + """Brk's mempool txid list must not contain duplicates.""" + b = brk.get_json("/api/mempool/txids") + show("GET", "/api/mempool/txids", f"({len(b)} txids)", "—") + assert len(b) == len(set(b)), ( + f"duplicate txids: {len(b) - len(set(b))} duplicates out of {len(b)}" + ) + + +def test_mempool_txids_count_matches_summary(brk): + """`/api/mempool/txids` length must match `/api/mempool`'s `count` field.""" + txids = brk.get_json("/api/mempool/txids") + summary = brk.get_json("/api/mempool") + show("GET", "/api/mempool/txids", f"len={len(txids)}", f"count={summary.get('count')}") + # Allow a small drift (1-2) since the mempool is updated asynchronously + # between the two fetches. + assert abs(len(txids) - summary["count"]) <= 5, ( + f"txids={len(txids)} vs /api/mempool.count={summary['count']}" + ) diff --git a/packages/brk_client/tests/mempool_compat/mining/conftest.py b/packages/brk_client/tests/mempool_compat/mining/conftest.py new file mode 100644 index 000000000..015b3ebbc --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/conftest.py @@ -0,0 +1,17 @@ +"""Mining-specific fixtures shared by every mining test in this folder.""" + +import pytest + + +@pytest.fixture(scope="module") +def pool_slugs(mempool): + """Top 3 active pool slugs from the last week.""" + data = mempool.get_json("/api/v1/mining/pools/1w") + pools = data.get("pools", []) if isinstance(data, dict) else [] + slugs = [p["slug"] for p in pools if p.get("blockCount", 0) > 0][:3] + return slugs or ["foundryusa"] + + +@pytest.fixture(scope="module") +def pool_slug(pool_slugs): + return pool_slugs[0] diff --git a/packages/brk_client/tests/mempool_compat/mining/test_blocks_fee_rates.py b/packages/brk_client/tests/mempool_compat/mining/test_blocks_fee_rates.py new file mode 100644 index 000000000..8bfbac7bf --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_blocks_fee_rates.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/blocks/fee-rates/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) +def test_mining_blocks_fee_rates(brk, mempool, period): + """Block fee-rate percentiles must have the same element structure.""" + path = f"/api/v1/mining/blocks/fee-rates/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_blocks_fees.py b/packages/brk_client/tests/mempool_compat/mining/test_blocks_fees.py new file mode 100644 index 000000000..c7873d4ac --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_blocks_fees.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/blocks/fees/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) +def test_mining_blocks_fees(brk, mempool, period): + """Average block fees must have the same element structure.""" + path = f"/api/v1/mining/blocks/fees/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_blocks_rewards.py b/packages/brk_client/tests/mempool_compat/mining/test_blocks_rewards.py new file mode 100644 index 000000000..987e85c37 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_blocks_rewards.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/blocks/rewards/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) +def test_mining_blocks_rewards(brk, mempool, period): + """Average block rewards must have the same element structure.""" + path = f"/api/v1/mining/blocks/rewards/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_blocks_sizes_weights.py b/packages/brk_client/tests/mempool_compat/mining/test_blocks_sizes_weights.py new file mode 100644 index 000000000..aa588052c --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_blocks_sizes_weights.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/blocks/sizes-weights/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) +def test_mining_blocks_sizes_weights(brk, mempool, period): + """Block sizes and weights must have the same structure.""" + path = f"/api/v1/mining/blocks/sizes-weights/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_blocks_timestamp.py b/packages/brk_client/tests/mempool_compat/mining/test_blocks_timestamp.py new file mode 100644 index 000000000..0257da575 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_blocks_timestamp.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/blocks/timestamp/{timestamp}""" + +from _lib import assert_same_structure, show + + +def test_mining_blocks_timestamp(brk, mempool, live): + """Block lookup by timestamp must have the same structure for various eras.""" + for block in live.blocks: + info = brk.get_json(f"/api/block/{block.hash}") + ts = info["timestamp"] + path = f"/api/v1/mining/blocks/timestamp/{ts}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_difficulty_adjustments.py b/packages/brk_client/tests/mempool_compat/mining/test_difficulty_adjustments.py new file mode 100644 index 000000000..4dfe03dc4 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_difficulty_adjustments.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/difficulty-adjustments/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y"]) +def test_mining_difficulty_adjustments(brk, mempool, period): + """Historical difficulty adjustments must have the same structure.""" + path = f"/api/v1/mining/difficulty-adjustments/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_hashrate.py b/packages/brk_client/tests/mempool_compat/mining/test_hashrate.py new file mode 100644 index 000000000..c40345b83 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_hashrate.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/hashrate/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y"]) +def test_mining_hashrate(brk, mempool, period): + """Network hashrate + difficulty must have the same structure.""" + path = f"/api/v1/mining/hashrate/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_hashrate_pools.py b/packages/brk_client/tests/mempool_compat/mining/test_hashrate_pools.py new file mode 100644 index 000000000..81548da9d --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_hashrate_pools.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/hashrate/pools/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "1y"]) +def test_mining_hashrate_pools(brk, mempool, period): + """Per-pool hashrate must have the same structure.""" + path = f"/api/v1/mining/hashrate/pools/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_pool.py b/packages/brk_client/tests/mempool_compat/mining/test_pool.py new file mode 100644 index 000000000..ea9cf6d68 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_pool.py @@ -0,0 +1,13 @@ +"""GET /api/v1/mining/pool/{slug}""" + +from _lib import assert_same_structure, show, summary + + +def test_mining_pool_detail(brk, mempool, pool_slugs): + """Pool detail must have the same structure for top pools.""" + for slug in pool_slugs: + path = f"/api/v1/mining/pool/{slug}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_pool_blocks.py b/packages/brk_client/tests/mempool_compat/mining/test_pool_blocks.py new file mode 100644 index 000000000..81981c0ba --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_pool_blocks.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/pool/{slug}/blocks""" + +from _lib import assert_same_structure, show + + +def test_mining_pool_blocks(brk, mempool, pool_slugs): + """Recent blocks by pool must have the same element structure.""" + for slug in pool_slugs: + path = f"/api/v1/mining/pool/{slug}/blocks" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") + assert isinstance(b, list) and isinstance(m, list) + if b and m: + assert_same_structure(b[0], m[0]) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_pool_blocks_height.py b/packages/brk_client/tests/mempool_compat/mining/test_pool_blocks_height.py new file mode 100644 index 000000000..7e7fbaeda --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_pool_blocks_height.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/pool/{slug}/blocks/{height}""" + +from _lib import assert_same_structure, show + + +def test_mining_pool_blocks_at_height(brk, mempool, pool_slug, live): + """Pool blocks before various heights must have the same element structure.""" + for block in live.blocks[::2]: # every other block, to keep run-time bounded + path = f"/api/v1/mining/pool/{pool_slug}/blocks/{block.height}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") + assert isinstance(b, list) and isinstance(m, list) + if b and m: + assert_same_structure(b[0], m[0]) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_pool_hashrate.py b/packages/brk_client/tests/mempool_compat/mining/test_pool_hashrate.py new file mode 100644 index 000000000..5ddf7c464 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_pool_hashrate.py @@ -0,0 +1,13 @@ +"""GET /api/v1/mining/pool/{slug}/hashrate""" + +from _lib import assert_same_structure, show, summary + + +def test_mining_pool_hashrate(brk, mempool, pool_slugs): + """Pool hashrate history must have the same structure for top pools.""" + for slug in pool_slugs: + path = f"/api/v1/mining/pool/{slug}/hashrate" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_pools.py b/packages/brk_client/tests/mempool_compat/mining/test_pools.py new file mode 100644 index 000000000..0fd34aee0 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_pools.py @@ -0,0 +1,45 @@ +"""GET /api/v1/mining/pools""" + +from _lib import assert_same_structure, show + + +def test_mining_pools_list_structure(brk, mempool): + """Pool list must have the same element structure.""" + path = "/api/v1/mining/pools" + b = brk.get_json(path) + m = mempool.get_json(path) + show( + "GET", path, + b[:3] if isinstance(b, list) else b, + m[:3] if isinstance(m, list) else m, + ) + assert_same_structure(b, m) + + +def _pools(data): + """`pools` may live at the root or inside an envelope across versions.""" + if isinstance(data, list): + return data + return data.get("pools", []) if isinstance(data, dict) else [] + + +def test_mining_pools_list_fields(brk): + """Each pool entry must carry slug and name (period-less endpoint omits stats).""" + b = _pools(brk.get_json("/api/v1/mining/pools")) + show("GET", "/api/v1/mining/pools", f"({len(b)} pools)", "—") + assert b, "no pools in brk's response" + required = {"slug", "name"} + for p in b[:5]: + missing = required - set(p.keys()) + assert not missing, f"pool {p.get('slug', '?')} missing fields: {missing}" + assert isinstance(p["name"], str) and p["name"] + + +def test_mining_pools_slugs_unique(brk): + """Pool slugs must be unique across the response.""" + b = _pools(brk.get_json("/api/v1/mining/pools")) + slugs = [p["slug"] for p in b] + show("GET", "/api/v1/mining/pools", f"({len(slugs)} slugs)", "—") + assert len(slugs) == len(set(slugs)), ( + f"duplicate slugs: {len(slugs) - len(set(slugs))}" + ) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_pools_period.py b/packages/brk_client/tests/mempool_compat/mining/test_pools_period.py new file mode 100644 index 000000000..f7df577cb --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_pools_period.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/pools/{time_period}""" + +import pytest + +from _lib import assert_same_structure, show, summary + + +@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y", "all"]) +def test_mining_pools_by_period(brk, mempool, period): + """Pool stats for a time period must have the same structure.""" + path = f"/api/v1/mining/pools/{period}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, summary(b), summary(m)) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/mining/test_reward_stats.py b/packages/brk_client/tests/mempool_compat/mining/test_reward_stats.py new file mode 100644 index 000000000..fbdbc1269 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/mining/test_reward_stats.py @@ -0,0 +1,15 @@ +"""GET /api/v1/mining/reward-stats/{block_count}""" + +import pytest + +from _lib import assert_same_structure, show + + +@pytest.mark.parametrize("block_count", [10, 100, 500]) +def test_mining_reward_stats(brk, mempool, block_count): + """Reward stats must have the same structure.""" + path = f"/api/v1/mining/reward-stats/{block_count}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_cpfp.py b/packages/brk_client/tests/mempool_compat/transactions/test_cpfp.py new file mode 100644 index 000000000..b5f507924 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_cpfp.py @@ -0,0 +1,12 @@ +"""GET /api/v1/cpfp/{txid}""" + +from _lib import assert_same_structure, show + + +def test_cpfp(brk, mempool, block): + """CPFP info structure must match for a confirmed tx.""" + path = f"/api/v1/cpfp/{block.txid}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_post_tx.py b/packages/brk_client/tests/mempool_compat/transactions/test_post_tx.py new file mode 100644 index 000000000..05548edf9 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_post_tx.py @@ -0,0 +1,40 @@ +"""POST /api/tx (broadcast) + +We can't actually broadcast a real transaction in a test, so we send a +clearly malformed payload and verify both servers reject it with 4xx. The +goal is to confirm the endpoint exists and behaves like a transaction +broadcaster — not to push live transactions. +""" + +from _lib import show + + +def test_post_tx_invalid_hex(brk, mempool): + """Both servers must reject an obviously invalid hex payload with 4xx.""" + path = "/api/tx" + bad_hex = "deadbeef" # too short to be a valid serialized transaction + + b = brk.session.post(f"{brk.base_url}{path}", data=bad_hex, timeout=15) + mempool._wait() + m = mempool.session.post(f"{mempool.base_url}{path}", data=bad_hex, timeout=15) + show("POST", path, f"brk={b.status_code}", f"mempool={m.status_code}") + + assert 400 <= b.status_code < 500, ( + f"brk POST /api/tx with garbage should 4xx, got {b.status_code}: {b.text!r}" + ) + assert 400 <= m.status_code < 500, ( + f"mempool POST /api/tx with garbage should 4xx, got {m.status_code}: {m.text!r}" + ) + + +def test_post_tx_empty_body(brk, mempool): + """Both servers must reject an empty body with 4xx.""" + path = "/api/tx" + + b = brk.session.post(f"{brk.base_url}{path}", data="", timeout=15) + mempool._wait() + m = mempool.session.post(f"{mempool.base_url}{path}", data="", timeout=15) + show("POST", path, f"brk={b.status_code}", f"mempool={m.status_code}") + + assert 400 <= b.status_code < 500 + assert 400 <= m.status_code < 500 diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_transaction_times.py b/packages/brk_client/tests/mempool_compat/transactions/test_transaction_times.py new file mode 100644 index 000000000..c506a9c27 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_transaction_times.py @@ -0,0 +1,56 @@ +"""GET /api/v1/transaction-times?txId[]=...""" + +from _lib import show + + +def test_transaction_times_few(brk, mempool, live): + """First-seen timestamps must match for a few txids.""" + txids = [b.txid for b in live.blocks[:3]] + params = [("txId[]", t) for t in txids] + path = "/api/v1/transaction-times" + b = brk.get_json(path, params=params) + m = mempool.get_json(path, params=params) + show("GET", f"{path}?txId[]={{{len(txids)} txids}}", b, m) + assert isinstance(b, list) and isinstance(m, list) + assert len(b) == len(m) == len(txids) + assert b == m, f"timestamps differ: brk={b} vs mempool={m}" + + +def test_transaction_times_many(brk, mempool, live): + """A larger batch (covering all sample blocks + coinbases) must match exactly.""" + txids = [b.txid for b in live.blocks] + [b.coinbase_txid for b in live.blocks] + params = [("txId[]", t) for t in txids] + path = "/api/v1/transaction-times" + b = brk.get_json(path, params=params) + m = mempool.get_json(path, params=params) + show("GET", f"{path}?txId[]={{{len(txids)} txids}}", f"({len(b)})", f"({len(m)})") + assert len(b) == len(m) == len(txids) + assert b == m, f"timestamps differ: brk={b} vs mempool={m}" + + +def test_transaction_times_single(brk, mempool, live): + """A single-element batch must return a 1-element list with the same value.""" + txid = live.sample_txid + params = [("txId[]", txid)] + path = "/api/v1/transaction-times" + b = brk.get_json(path, params=params) + m = mempool.get_json(path, params=params) + show("GET", f"{path}?txId[]={txid[:16]}...", b, m) + assert isinstance(b, list) and isinstance(m, list) + assert len(b) == len(m) == 1 + assert b == m, f"single timestamp differs: brk={b} vs mempool={m}" + + +def test_transaction_times_empty(brk, mempool): + """An empty batch must be rejected (any non-2xx) on both servers. + + mempool.space returns 500 — technically a server-side bug (it should be a + 4xx since the request itself is malformed) — so we don't insist on exact + status parity, only that neither server silently treats it as valid input. + """ + path = "/api/v1/transaction-times" + b_resp = brk.get_raw(path) + m_resp = mempool.get_raw(path) + show("GET", path, f"brk={b_resp.status_code}", f"mempool={m_resp.status_code}") + assert not b_resp.ok, f"brk accepted empty batch with {b_resp.status_code}: {b_resp.text!r}" + assert not m_resp.ok, f"mempool accepted empty batch with {m_resp.status_code}" diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx.py new file mode 100644 index 000000000..302248c48 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx.py @@ -0,0 +1,21 @@ +"""GET /api/tx/{txid}""" + +from _lib import assert_same_values, show + + +def test_tx_by_id(brk, mempool, block): + """Full transaction data must match for a confirmed tx.""" + path = f"/api/tx/{block.txid}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m, exclude={"sigops"}) + + +def test_tx_coinbase(brk, mempool, block): + """Coinbase transaction must match.""" + path = f"/api/tx/{block.coinbase_txid}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m, exclude={"sigops"}) diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_hex.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_hex.py new file mode 100644 index 000000000..2b9b75a70 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_hex.py @@ -0,0 +1,12 @@ +"""GET /api/tx/{txid}/hex""" + +from _lib import show + + +def test_tx_hex(brk, mempool, block): + """Raw transaction hex must be identical.""" + path = f"/api/tx/{block.txid}/hex" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b[:80] + "...", m[:80] + "...") + assert b == m diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_merkle_proof.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_merkle_proof.py new file mode 100644 index 000000000..8ec9b949c --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_merkle_proof.py @@ -0,0 +1,12 @@ +"""GET /api/tx/{txid}/merkle-proof""" + +from _lib import assert_same_values, show + + +def test_tx_merkle_proof(brk, mempool, block): + """Merkle inclusion proof must match.""" + path = f"/api/tx/{block.txid}/merkle-proof" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m) diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_merkleblock_proof.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_merkleblock_proof.py new file mode 100644 index 000000000..166e827d4 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_merkleblock_proof.py @@ -0,0 +1,12 @@ +"""GET /api/tx/{txid}/merkleblock-proof""" + +from _lib import show + + +def test_tx_merkleblock_proof(brk, mempool, block): + """BIP37 merkleblock proof hex must be identical.""" + path = f"/api/tx/{block.txid}/merkleblock-proof" + b = brk.get_text(path) + m = mempool.get_text(path) + show("GET", path, b[:80] + "...", m[:80] + "...") + assert b == m diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_outspend.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_outspend.py new file mode 100644 index 000000000..2e967e3ab --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_outspend.py @@ -0,0 +1,38 @@ +"""GET /api/tx/{txid}/outspend/{vout}""" + +from _lib import assert_same_values, show + + +def test_tx_outspend_first(brk, mempool, block): + """Spending status of vout 0 must match exactly.""" + path = f"/api/tx/{block.txid}/outspend/0" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m) + + +def test_tx_outspend_last(brk, mempool, block): + """Spending status of the last vout must also match exactly.""" + tx = mempool.get_json(f"/api/tx/{block.txid}") + last_vout = len(tx["vout"]) - 1 + path = f"/api/tx/{block.txid}/outspend/{last_vout}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m) + + +def test_tx_outspend_out_of_range(brk, mempool, block): + """A vout index past the last output must produce the same response on both servers. + + Both servers return `{"spent": false}` rather than 4xx — they don't bound-check + the vout index. The compat property is that they agree. + """ + tx = mempool.get_json(f"/api/tx/{block.txid}") + bad_vout = len(tx["vout"]) + 100 + path = f"/api/tx/{block.txid}/outspend/{bad_vout}" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert b == m, f"out-of-range outspend disagrees: brk={b} vs mempool={m}" diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_outspends.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_outspends.py new file mode 100644 index 000000000..320538b30 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_outspends.py @@ -0,0 +1,12 @@ +"""GET /api/tx/{txid}/outspends""" + +from _lib import assert_same_values, show + + +def test_tx_outspends(brk, mempool, block): + """Spending status of all outputs must match exactly.""" + path = f"/api/tx/{block.txid}/outspends" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m) diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_raw.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_raw.py new file mode 100644 index 000000000..6c992c2ae --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_raw.py @@ -0,0 +1,12 @@ +"""GET /api/tx/{txid}/raw""" + +from _lib import show + + +def test_tx_raw(brk, mempool, block): + """Raw transaction bytes must be identical.""" + path = f"/api/tx/{block.txid}/raw" + b = brk.get_bytes(path) + m = mempool.get_bytes(path) + show("GET", path, f"<{len(b)} bytes>", f"<{len(m)} bytes>") + assert b == m diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_rbf.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_rbf.py new file mode 100644 index 000000000..424fbbafc --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_rbf.py @@ -0,0 +1,16 @@ +"""GET /api/v1/tx/{txid}/rbf + +For confirmed transactions both servers return an empty/null replacement +set; the structure is what's load-bearing here. +""" + +from _lib import assert_same_structure, show + + +def test_tx_rbf_for_confirmed(brk, mempool, block): + """RBF replacement timeline structure must match for a confirmed tx.""" + path = f"/api/v1/tx/{block.txid}/rbf" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_structure(b, m) diff --git a/packages/brk_client/tests/mempool_compat/transactions/test_tx_status.py b/packages/brk_client/tests/mempool_compat/transactions/test_tx_status.py new file mode 100644 index 000000000..8f6f64643 --- /dev/null +++ b/packages/brk_client/tests/mempool_compat/transactions/test_tx_status.py @@ -0,0 +1,12 @@ +"""GET /api/tx/{txid}/status""" + +from _lib import assert_same_values, show + + +def test_tx_status(brk, mempool, block): + """Confirmation status must match for a confirmed tx.""" + path = f"/api/tx/{block.txid}/status" + b = brk.get_json(path) + m = mempool.get_json(path) + show("GET", path, b, m) + assert_same_values(b, m) diff --git a/packages/brk_client/tests/test_basic.py b/packages/brk_client/tests/test_basic.py index 61dfc452f..807dad4fb 100644 --- a/packages/brk_client/tests/test_basic.py +++ b/packages/brk_client/tests/test_basic.py @@ -57,3 +57,17 @@ def test_fetch_typed_series(): print(e) f = client.series.prices.ohlc.usd.by.day1().tail(10).fetch() print(f) + + +def test_endpoint_len(): + client = BrkClient("http://localhost:3110") + n = client.series.prices.split.close.usd.by.day1().len() + assert isinstance(n, int) + assert n > 0 + + +def test_endpoint_version(): + client = BrkClient("http://localhost:3110") + v = client.series.prices.split.close.usd.by.day1().version() + assert isinstance(v, int) + assert v >= 1 diff --git a/packages/brk_client/tests/test_metric_data.py b/packages/brk_client/tests/test_metric_data.py index efbae126b..894816d29 100644 --- a/packages/brk_client/tests/test_metric_data.py +++ b/packages/brk_client/tests/test_metric_data.py @@ -18,7 +18,6 @@ def day1_metric(): version=1, index="day1", type="n", - total=100, start=0, end=5, stamp="2024-01-01T00:00:00Z", @@ -33,7 +32,6 @@ def height_metric(): version=1, index="height", type="n", - total=1000, start=800000, end=800005, stamp="2024-01-01T00:00:00Z", @@ -48,7 +46,6 @@ def month1_metric(): version=1, index="month1", type="n", - total=200, start=0, end=3, stamp="2024-01-01T00:00:00Z", @@ -63,7 +60,6 @@ def hour1_metric(): version=1, index="hour1", type="n", - total=200000, start=0, end=3, stamp="2024-01-01T00:00:00Z", @@ -78,7 +74,6 @@ def week1_metric(): version=1, index="week1", type="n", - total=800, start=0, end=3, stamp="2024-01-01T00:00:00Z", @@ -93,7 +88,6 @@ def year1_metric(): version=1, index="year1", type="n", - total=20, start=0, end=3, stamp="2024-01-01T00:00:00Z", @@ -108,7 +102,6 @@ def day3_metric(): version=1, index="day3", type="n", - total=2000, start=0, end=3, stamp="2024-01-01T00:00:00Z", @@ -123,7 +116,6 @@ def empty_metric(): version=1, index="day1", type="n", - total=100, start=5, end=5, stamp="2024-01-01T00:00:00Z", diff --git a/packages/brk_client/uv.lock b/packages/brk_client/uv.lock index 19e5e4a29..37f1036f4 100644 --- a/packages/brk_client/uv.lock +++ b/packages/brk_client/uv.lock @@ -50,7 +50,7 @@ wheels = [ [[package]] name = "brk-client" -version = "0.3.0b6" +version = "0.3.0b7" source = { editable = "." } [package.dev-dependencies] diff --git a/scripts/mempool_compat/conftest.py b/scripts/mempool_compat/conftest.py deleted file mode 100644 index c49d72027..000000000 --- a/scripts/mempool_compat/conftest.py +++ /dev/null @@ -1,338 +0,0 @@ -""" -Mempool.space API compatibility tests. - -Compares every brk mempool_space endpoint against the real mempool.space API -using live blockchain data — nothing is hardcoded or deterministic. - -Usage: - cd scripts/mempool_compat - uv run pytest -sv # all tests, verbose - uv run pytest -sv test_blocks.py # one category - uv run pytest -sv -k "test_block_header" # one test - BRK_URL=http://host:port uv run pytest -sv # custom brk server - -Environment variables: - BRK_URL brk server base URL (default: http://localhost:3000) - MEMPOOL_URL mempool.space base URL (default: https://mempool.space) - RATE_LIMIT seconds between mempool.space requests (default: 0.5) -""" - -import json -import os -import time -from dataclasses import dataclass -from typing import Any, Optional, Set - -import pytest -import requests - -BRK_BASE = os.environ.get("BRK_URL", "http://localhost:3000") -MEMPOOL_BASE = os.environ.get("MEMPOOL_URL", "https://mempool.space") -RATE_LIMIT = float(os.environ.get("RATE_LIMIT", "0.5")) - - -# ── API client ──────────────────────────────────────────────────────── - - -class ApiClient: - """HTTP client for a single API server with optional rate limiting.""" - - def __init__(self, base_url: str, name: str, rate_limit: float = 0.0): - self.base_url = base_url.rstrip("/") - self.name = name - self.rate_limit = rate_limit - self._last_request = 0.0 - self.session = requests.Session() - self.session.headers["User-Agent"] = "brk-compat-test/1.0" - - def _wait(self): - if self.rate_limit > 0: - elapsed = time.monotonic() - self._last_request - if elapsed < self.rate_limit: - time.sleep(self.rate_limit - elapsed) - self._last_request = time.monotonic() - - def get(self, path: str, params=None, timeout: int = 30) -> requests.Response: - self._wait() - url = f"{self.base_url}{path}" - for attempt in range(3): - resp = self.session.get(url, params=params, timeout=timeout) - if resp.status_code == 429: - wait = int(resp.headers.get("Retry-After", 5)) - time.sleep(wait) - continue - resp.raise_for_status() - return resp - resp.raise_for_status() - return resp - - def get_json(self, path: str, params=None, timeout: int = 30) -> Any: - return self.get(path, params=params, timeout=timeout).json() - - def get_text(self, path: str, params=None, timeout: int = 30) -> str: - return self.get(path, params=params, timeout=timeout).text - - def get_bytes(self, path: str, params=None, timeout: int = 30) -> bytes: - return self.get(path, params=params, timeout=timeout).content - - -# ── Live data ───────────────────────────────────────────────────────── - - -# Absolute heights for well-known eras + relative depths for recent blocks. -# Covers: genesis-era, early, mid, post-halving, taproot-era, recent, near-tip. -FIXED_HEIGHTS = [100, 100_000, 400_000, 630_000, 800_000] -RELATIVE_DEPTHS = [1000, 100, 10] - - -@dataclass -class BlockData: - """A discovered block with associated txids.""" - - height: int - hash: str - txid: str - coinbase_txid: str - - -@dataclass -class LiveData: - """Live blockchain data discovered at session start.""" - - tip_height: int - tip_hash: str - # Multiple blocks at various depths for parametrized tests - blocks: list # list[BlockData] - # Addresses keyed by scriptpubkey_type - addresses: dict # dict[str, str] - # Convenience aliases (first block) - stable_height: int - stable_hash: str - stable_block: dict - sample_txid: str - coinbase_txid: str - sample_address: str - - -# ── Fixtures ────────────────────────────────────────────────────────── - - -@pytest.fixture(scope="session") -def brk(): - return ApiClient(BRK_BASE, "brk") - - -@pytest.fixture(scope="session") -def mempool(): - return ApiClient(MEMPOOL_BASE, "mempool.space", rate_limit=RATE_LIMIT) - - -@pytest.fixture(scope="session", autouse=True) -def check_servers(brk, mempool): - """Fail fast if either server is unreachable.""" - try: - brk.get("/api/blocks/tip/height") - except Exception as e: - pytest.exit(f"brk server not reachable at {brk.base_url}: {e}") - try: - mempool.get("/api/blocks/tip/height") - except Exception as e: - pytest.exit(f"mempool.space not reachable at {mempool.base_url}: {e}") - - -@pytest.fixture(scope="session") -def live(mempool) -> LiveData: - """Discover live blockchain data for all tests. - - Fetches blocks at several depths and extracts txids + addresses of - different types so parametrized tests hit varied real data. - """ - tip_height = int(mempool.get_text("/api/blocks/tip/height")) - tip_hash = mempool.get_text("/api/blocks/tip/hash") - - heights = FIXED_HEIGHTS + [tip_height - d for d in RELATIVE_DEPTHS] - heights.sort() - - blocks: list[BlockData] = [] - addresses: dict[str, str] = {} - - for h in heights: - bh = mempool.get_text(f"/api/block-height/{h}") - txids = mempool.get_json(f"/api/block/{bh}/txids") - coinbase = txids[0] - sample = txids[min(1, len(txids) - 1)] - blocks.append(BlockData(height=h, hash=bh, txid=sample, coinbase_txid=coinbase)) - - # Collect addresses of different types from non-coinbase outputs - if len(addresses) < 8: - tx = mempool.get_json(f"/api/tx/{sample}") - for vout in tx.get("vout", []): - atype = vout.get("scriptpubkey_type") - addr = vout.get("scriptpubkey_address") - if addr and atype and atype not in addresses: - addresses[atype] = addr - - stable = blocks[0] - stable_block = mempool.get_json(f"/api/block/{stable.hash}") - sample_address = next(iter(addresses.values()), "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa") - - data = LiveData( - tip_height=tip_height, - tip_hash=tip_hash, - blocks=blocks, - addresses=addresses, - stable_height=stable.height, - stable_hash=stable.hash, - stable_block=stable_block, - sample_txid=stable.txid, - coinbase_txid=stable.coinbase_txid, - sample_address=sample_address, - ) - - print(f"\n{'='*70}") - print(f" LIVE TEST DATA (from {MEMPOOL_BASE})") - print(f"{'='*70}") - print(f" tip {data.tip_height} {data.tip_hash[:20]}…") - for i, b in enumerate(blocks): - print(f" block[{i}] {b.height} {b.hash[:20]}… tx={b.txid[:16]}…") - for atype, addr in addresses.items(): - print(f" addr {atype:12s} {addr}") - print(f"{'='*70}\n") - return data - - -# ── Display helpers ─────────────────────────────────────────────────── - - -def show(method: str, path: str, brk_data: Any, mem_data: Any, max_lines: int = 20): - """Print both responses so the runner can see what was fetched.""" - print(f"\n{'─'*70}") - print(f" {method} {path}") - print(f"{'─'*70}") - for label, data in [("mempool.space", mem_data), ("brk", brk_data)]: - print(f"\n [{label}]") - if isinstance(data, (dict, list)): - text = json.dumps(data, indent=2) - elif isinstance(data, bytes): - text = f"<{len(data)} bytes>" - else: - text = str(data) - lines = text.split("\n") - for line in lines[:max_lines]: - print(f" {line}") - if len(lines) > max_lines: - print(f" … ({len(lines) - max_lines} more lines)") - - -# ── Comparison helpers ──────────────────────────────────────────────── - -# Keys that brk is intentionally not implementing (mempool.space-specific features). -# Everything else that mempool.space returns MUST be present in brk. -ALLOWED_MISSING = { - "matchRate", "expectedFees", "expectedWeight", - # brk only tracks USD — non-USD currencies and exchange rates are intentionally absent - "EUR", "GBP", "CAD", "CHF", "AUD", "JPY", - "USDEUR", "USDGBP", "USDCAD", "USDCHF", "USDAUD", "USDJPY", - # brk doesn't compute block health scores - "avgBlockHealth", - # brk doesn't compute block similarity/template matching - "similarity", - # brk doesn't compute fee delta or match rate per pool - "avgFeeDelta", "avgMatchRate", -} - -# Coinbase transactions use vout=65535 (u16::MAX) in brk vs 4294967295 (u32::MAX) -# in mempool.space. This is an intentional representation difference. -COINBASE_VOUT_BRK = 65535 -COINBASE_VOUT_MEMPOOL = 4294967295 - - -def assert_same_structure(brk_data: Any, mem_data: Any, path: str = "root"): - """brk must have every key mempool.space has (extra brk keys are fine). - - Recurses into nested dicts; for arrays, compares the first element. - int/float are treated as equivalent; None is compatible with anything. - """ - if isinstance(mem_data, dict): - assert isinstance(brk_data, dict), ( - f"Expected dict at {path}, got {type(brk_data).__name__}" - ) - brk_keys = set(brk_data.keys()) - mem_keys = set(mem_data.keys()) - missing = mem_keys - brk_keys - ALLOWED_MISSING - assert not missing, f"brk missing keys at {path}: {missing}" - for key in brk_keys & mem_keys: - assert_same_structure(brk_data[key], mem_data[key], f"{path}.{key}") - elif isinstance(mem_data, list): - assert isinstance(brk_data, list), ( - f"Expected list at {path}, got {type(brk_data).__name__}" - ) - if mem_data and brk_data: - assert_same_structure(brk_data[0], mem_data[0], f"{path}[0]") - else: - if mem_data is None or brk_data is None: - return - bt = type(brk_data).__name__ - mt = type(mem_data).__name__ - if {bt, mt} <= {"int", "float"}: - return - # int/str are compatible when the string is a numeric literal - # (mempool.space serializes large numbers as strings) - if {bt, mt} == {"int", "str"}: - return - assert bt == mt, ( - f"Type mismatch at {path}: brk={bt}({brk_data!r}) " - f"vs mempool={mt}({mem_data!r})" - ) - - -def assert_same_values( - brk_data: Any, - mem_data: Any, - path: str = "root", - exclude: Optional[Set[str]] = None, -): - """Both responses must have identical values. - - Floats are compared with relative tolerance 1e-4. - Pass ``exclude`` to skip keys that are expected to differ. - """ - exclude = exclude or set() - - if isinstance(mem_data, dict): - assert isinstance(brk_data, dict), ( - f"Expected dict at {path}, got {type(brk_data).__name__}" - ) - # brk must have every mempool key; extra brk keys are fine - mem_keys = set(mem_data.keys()) - for key in mem_keys - exclude - ALLOWED_MISSING: - assert key in brk_data, f"brk missing '{key}' at {path}" - assert_same_values(brk_data[key], mem_data[key], f"{path}.{key}", exclude) - elif isinstance(mem_data, list): - assert isinstance(brk_data, list), ( - f"Expected list at {path}, got {type(brk_data).__name__}" - ) - assert len(brk_data) == len(mem_data), ( - f"Length mismatch at {path}: brk={len(brk_data)} vs mempool={len(mem_data)}" - ) - for i, (b, m) in enumerate(zip(brk_data, mem_data)): - assert_same_values(b, m, f"{path}[{i}]", exclude) - elif mem_data is None: - # mempool returns null, brk computes a value — that's fine - return - elif isinstance(mem_data, float) or isinstance(brk_data, float): - if brk_data is None: - return - assert float(brk_data) == pytest.approx( - float(mem_data), rel=1e-4, abs=1e-6 - ), f"Float mismatch at {path}: brk={brk_data} vs mempool={mem_data}" - else: - # Coinbase vout: brk uses u16::MAX, mempool uses u32::MAX — both valid - if ( - brk_data == COINBASE_VOUT_BRK - and mem_data == COINBASE_VOUT_MEMPOOL - ): - return - assert brk_data == mem_data, ( - f"Value mismatch at {path}: brk={brk_data!r} vs mempool={mem_data!r}" - ) diff --git a/scripts/mempool_compat/pyproject.toml b/scripts/mempool_compat/pyproject.toml deleted file mode 100644 index 49006a4b1..000000000 --- a/scripts/mempool_compat/pyproject.toml +++ /dev/null @@ -1,11 +0,0 @@ -[project] -name = "mempool-compat" -version = "0.1.0" -requires-python = ">=3.9" -dependencies = [ - "pytest>=7.0", - "requests>=2.28", -] - -[tool.pytest.ini_options] -testpaths = ["."] diff --git a/scripts/mempool_compat/test_addresses.py b/scripts/mempool_compat/test_addresses.py deleted file mode 100644 index 7d81500b6..000000000 --- a/scripts/mempool_compat/test_addresses.py +++ /dev/null @@ -1,159 +0,0 @@ -""" -Address endpoint compatibility tests — parametrized across address types. - -Endpoints covered: - GET /api/address/{address} - GET /api/address/{address}/txs - GET /api/address/{address}/txs/chain - GET /api/address/{address}/txs/mempool - GET /api/address/{address}/utxo - GET /api/v1/validate-address/{address} -""" - -import pytest - -from conftest import show, assert_same_structure, assert_same_values - - -@pytest.fixture(params=[ - "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", # P2PKH — early block reward - "3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", # P2SH -], ids=["p2pkh", "p2sh"]) -def static_addr(request): - """Well-known addresses that always exist.""" - return request.param - - -@pytest.fixture() -def live_addrs(live): - """All dynamically discovered address types.""" - return list(live.addresses.items()) - - -# ── /api/address/{address} ─────────────────────────────────────────── - - -def test_address_info_static(brk, mempool, static_addr): - """Address stats structure must match for well-known addresses.""" - path = f"/api/address/{static_addr}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - assert b["address"] == m["address"] - - -def test_address_info_discovered(brk, mempool, live_addrs): - """Address stats structure must match for each discovered type.""" - for atype, addr in live_addrs: - path = f"/api/address/{addr}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", f"{path} [{atype}]", b, m) - assert_same_structure(b, m) - assert b["address"] == m["address"] - - -def test_address_chain_stats_close(brk, mempool, live_addrs): - """Chain stats values must be close for each discovered address.""" - for atype, addr in live_addrs: - path = f"/api/address/{addr}" - b = brk.get_json(path)["chain_stats"] - m = mempool.get_json(path)["chain_stats"] - show("GET", f"{path} [chain_stats, {atype}]", b, m) - assert_same_structure(b, m) - assert abs(b["tx_count"] - m["tx_count"]) <= 5, ( - f"{atype} tx_count: brk={b['tx_count']} vs mempool={m['tx_count']}" - ) - - -# ── /api/address/{address}/txs ─────────────────────────────────────── - - -def test_address_txs(brk, mempool, static_addr): - """Address transaction list structure must match.""" - path = f"/api/address/{static_addr}/txs" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") - assert isinstance(b, list) and isinstance(m, list) - if b and m: - assert_same_structure(b[0], m[0]) - - -# ── /api/address/{address}/txs/chain ───────────────────────────────── - - -def test_address_txs_chain(brk, mempool, static_addr): - """Confirmed-only tx list structure must match.""" - path = f"/api/address/{static_addr}/txs/chain" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") - assert isinstance(b, list) and isinstance(m, list) - if b and m: - assert_same_structure(b[0], m[0]) - - -# ── /api/address/{address}/txs/mempool ──────────────────────────────── - - -def test_address_txs_mempool(brk, mempool, live): - """Mempool tx list must be an array (contents are volatile).""" - path = f"/api/address/{live.sample_address}/txs/mempool" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") - assert isinstance(b, list) and isinstance(m, list) - - -# ── /api/address/{address}/utxo ────────────────────────────────────── - - -def test_address_utxo(brk, mempool, static_addr): - """UTXO list must match — same txids, values, and statuses.""" - path = f"/api/address/{static_addr}/utxo" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} utxos)", f"({len(m)} utxos)") - assert isinstance(b, list) and isinstance(m, list) - # Sort by txid+vout for stable comparison - key = lambda u: (u.get("txid", ""), u.get("vout", 0)) - b_sorted = sorted(b, key=key) - m_sorted = sorted(m, key=key) - assert_same_values(b_sorted, m_sorted) - - -# ── /api/v1/validate-address/{address} ─────────────────────────────── - - -def test_validate_address_discovered(brk, mempool, live_addrs): - """Validation of each discovered address type must match exactly.""" - for atype, addr in live_addrs: - path = f"/api/v1/validate-address/{addr}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", f"{path} [{atype}]", b, m) - assert_same_values(b, m) - assert b["isvalid"] is True - - -def test_validate_address_p2pkh(brk, mempool): - """Satoshi's P2PKH address must validate identically.""" - path = "/api/v1/validate-address/1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m) - assert b["isvalid"] is True - - -def test_validate_address_invalid(brk, mempool): - """Invalid address must produce the same rejection structure.""" - path = "/api/v1/validate-address/notanaddress123" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert b["isvalid"] is False - assert m["isvalid"] is False - assert_same_structure(b, m) diff --git a/scripts/mempool_compat/test_blocks.py b/scripts/mempool_compat/test_blocks.py deleted file mode 100644 index b2710672f..000000000 --- a/scripts/mempool_compat/test_blocks.py +++ /dev/null @@ -1,265 +0,0 @@ -""" -Block endpoint compatibility tests — parametrized across blockchain eras. - -Endpoints covered: - GET /api/block/{hash} - GET /api/v1/block/{hash} (with extras) - GET /api/block/{hash}/header text/plain - GET /api/block/{hash}/status - GET /api/block/{hash}/txids - GET /api/block/{hash}/txs - GET /api/block/{hash}/txs/{start} - GET /api/block/{hash}/txid/{index} text/plain - GET /api/block-height/{height} text/plain - GET /api/blocks - GET /api/blocks/{height} - GET /api/v1/blocks - GET /api/v1/blocks/{height} - GET /api/blocks/tip/height text/plain - GET /api/blocks/tip/hash text/plain -""" - -import pytest - -from conftest import show, assert_same_structure, assert_same_values - - -def _block_ids(live): - return [f"h{b.height}" for b in live.blocks] - - -def _bi(request, live): - """Resolve parametrized block index, skip if out of range.""" - i = request.param - if i >= len(live.blocks): - pytest.skip("block not discovered") - return live.blocks[i] - - -@pytest.fixture(params=range(8), ids=[ - "h100", "h100k", "h400k", "h630k", "h800k", "recent1k", "recent100", "recent10", -]) -def block(request, live): - i = request.param - if i >= len(live.blocks): - pytest.skip("block not discovered") - return live.blocks[i] - - -# ── /api/block/{hash} ──────────────────────────────────────────────── - - -def test_block_by_hash(brk, mempool, block): - """Confirmed block info must be identical.""" - path = f"/api/block/{block.hash}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m) - - -# ── /api/v1/block/{hash} ───────────────────────────────────────────── - - -def test_block_v1_extras_all_values(brk, mempool, block): - """Every shared extras field must match — exposes computation differences.""" - path = f"/api/v1/block/{block.hash}" - b = brk.get_json(path)["extras"] - m = mempool.get_json(path)["extras"] - show("GET", f"{path} [extras]", b, m, max_lines=50) - assert_same_structure(b, m) - assert_same_values(b, m) - - -def test_block_v1_extras_pool(brk, mempool, block): - """Pool identification structure must match.""" - path = f"/api/v1/block/{block.hash}" - bp = brk.get_json(path)["extras"]["pool"] - mp = mempool.get_json(path)["extras"]["pool"] - show("GET", f"{path} [extras.pool]", bp, mp) - assert_same_structure(bp, mp) - - -# ── /api/block/{hash}/header ───────────────────────────────────────── - - -def test_block_header(brk, mempool, block): - """80-byte hex block header must be identical.""" - path = f"/api/block/{block.hash}/header" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b, m) - assert len(b) == 160, f"Expected 160 hex chars (80 bytes), got {len(b)}" - assert b == m - - -# ── /api/block/{hash}/status ───────────────────────────────────────── - - -def test_block_status(brk, mempool, block): - """Block status must be identical for a confirmed block.""" - path = f"/api/block/{block.hash}/status" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m) - - -# ── /api/block/{hash}/txids ────────────────────────────────────────── - - -def test_block_txids(brk, mempool, block): - """Ordered txid list must be identical.""" - path = f"/api/block/{block.hash}/txids" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b[:3], m[:3]) - assert b == m - - -# ── /api/block/{hash}/txs ──────────────────────────────────────────── - - -def test_block_txs_page0(brk, mempool, block): - """First page of block transactions must match.""" - path = f"/api/block/{block.hash}/txs" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") - assert len(b) == len(m), f"Page size: brk={len(b)} vs mempool={len(m)}" - if b and m: - assert_same_values(b[0], m[0], exclude={"sigops"}) - - -def test_block_txs_start_index(brk, mempool, block): - """Paginated txs from index 25 must match (skip small blocks).""" - # Blocks with <26 txs don't have a second page - txids = mempool.get_json(f"/api/block/{block.hash}/txids") - if len(txids) <= 25: - pytest.skip(f"block has only {len(txids)} txs") - path = f"/api/block/{block.hash}/txs/25" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)") - assert len(b) == len(m) - if b and m: - assert_same_structure(b[0], m[0]) - - -# ── /api/block/{hash}/txid/{index} ─────────────────────────────────── - - -def test_block_txid_at_index_0(brk, mempool, block): - """Txid at position 0 (coinbase) must match.""" - path = f"/api/block/{block.hash}/txid/0" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b, m) - assert b == m - - -def test_block_txid_at_index_1(brk, mempool, block): - """Txid at position 1 (first non-coinbase) must match.""" - txids = mempool.get_json(f"/api/block/{block.hash}/txids") - if len(txids) <= 1: - pytest.skip("block has only coinbase") - path = f"/api/block/{block.hash}/txid/1" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b, m) - assert b == m - - -def test_block_txid_at_last_index(brk, mempool, block): - """Txid at last position must match.""" - txids = mempool.get_json(f"/api/block/{block.hash}/txids") - last = len(txids) - 1 - path = f"/api/block/{block.hash}/txid/{last}" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b, m) - assert b == m - - -# ── /api/block-height/{height} ─────────────────────────────────────── - - -def test_block_height_to_hash(brk, mempool, block): - """Block hash at a given height must match.""" - path = f"/api/block-height/{block.height}" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b, m) - assert b == m - assert b == block.hash - - -# ── /api/blocks/{height} ───────────────────────────────────────────── - - -def test_blocks_from_height(brk, mempool, block): - """Confirmed blocks from a fixed height must match exactly.""" - path = f"/api/blocks/{block.height}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") - assert len(b) == len(m) - if b and m: - assert_same_values(b[0], m[0]) - - -def test_blocks_v1_from_height(brk, mempool, block): - """v1 blocks from a confirmed height — all values must match.""" - path = f"/api/v1/blocks/{block.height}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") - assert len(b) == len(m) - if b and m: - assert_same_values(b[0], m[0]) - - -# ── non-parametrized (no block param) ──────────────────────────────── - - -def test_blocks_recent(brk, mempool): - """Recent blocks list must have the same structure.""" - path = "/api/blocks" - b = brk.get_json(path) - m = mempool.get_json(path) - show( - "GET", path, - f"({len(b)} blocks, {b[-1]['height']}–{b[0]['height']})" if b else "[]", - f"({len(m)} blocks, {m[-1]['height']}–{m[0]['height']})" if m else "[]", - ) - assert len(b) > 0 - assert_same_structure(b, m) - - -def test_blocks_v1_recent(brk, mempool): - """Recent v1 blocks (with extras) must have the same structure.""" - path = "/api/v1/blocks" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") - assert len(b) > 0 - assert_same_structure(b, m) - - -def test_blocks_tip_height(brk, mempool): - """Tip heights must be within a few blocks of each other.""" - path = "/api/blocks/tip/height" - b = int(brk.get_text(path)) - m = int(mempool.get_text(path)) - show("GET", path, b, m) - assert abs(b - m) <= 3, f"Tip heights differ by {abs(b - m)}: brk={b}, mempool={m}" - - -def test_blocks_tip_hash(brk, mempool): - """Tip hash must be a valid 64-char hex string.""" - path = "/api/blocks/tip/hash" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b, m) - assert len(b) == 64 - assert len(m) == 64 diff --git a/scripts/mempool_compat/test_fees.py b/scripts/mempool_compat/test_fees.py deleted file mode 100644 index 923c13bcb..000000000 --- a/scripts/mempool_compat/test_fees.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Fee endpoint compatibility tests. - -Endpoints covered: - GET /api/v1/fees/recommended - GET /api/v1/fees/precise - GET /api/v1/fees/mempool-blocks -""" - -from conftest import show, assert_same_structure - - -EXPECTED_FEE_KEYS = [ - "fastestFee", "halfHourFee", "hourFee", "economyFee", "minimumFee", -] - - -# ── /api/v1/fees/recommended ───────────────────────────────────────── - - -def test_fees_recommended(brk, mempool): - """Recommended fees must have the same keys and numeric types.""" - path = "/api/v1/fees/recommended" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - for key in EXPECTED_FEE_KEYS: - assert key in b, f"brk missing '{key}'" - assert isinstance(b[key], (int, float)), f"'{key}' is not numeric: {type(b[key])}" - - -def test_fees_recommended_ordering(brk, mempool): - """Fee tiers must be ordered: fastest >= halfHour >= hour >= economy >= minimum.""" - path = "/api/v1/fees/recommended" - for label, client in [("brk", brk), ("mempool", mempool)]: - d = client.get_json(path) - assert d["fastestFee"] >= d["halfHourFee"] >= d["hourFee"], ( - f"{label}: fee ordering violated {d}" - ) - assert d["hourFee"] >= d["economyFee"] >= d["minimumFee"], ( - f"{label}: fee ordering violated {d}" - ) - - -# ── /api/v1/fees/precise ───────────────────────────────────────────── - - -def test_fees_precise(brk, mempool): - """Precise fees must have the same structure as recommended.""" - path = "/api/v1/fees/precise" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - for key in EXPECTED_FEE_KEYS: - assert key in b - - -# ── /api/v1/fees/mempool-blocks ────────────────────────────────────── - - -def test_fees_mempool_blocks(brk, mempool): - """Projected mempool blocks must have the same element structure.""" - path = "/api/v1/fees/mempool-blocks" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") - assert isinstance(b, list) and isinstance(m, list) - assert len(b) > 0 - if b and m: - assert_same_structure(b[0], m[0]) - - -def test_fees_mempool_blocks_fee_range(brk, mempool): - """Each projected block must have a 7-element feeRange.""" - path = "/api/v1/fees/mempool-blocks" - for label, client in [("brk", brk), ("mempool", mempool)]: - blocks = client.get_json(path) - for i, block in enumerate(blocks[:3]): - assert "feeRange" in block, f"{label} block {i} missing feeRange" - assert len(block["feeRange"]) == 7, ( - f"{label} block {i} feeRange has {len(block['feeRange'])} items, expected 7" - ) diff --git a/scripts/mempool_compat/test_general.py b/scripts/mempool_compat/test_general.py deleted file mode 100644 index d2a3fd7f5..000000000 --- a/scripts/mempool_compat/test_general.py +++ /dev/null @@ -1,122 +0,0 @@ -""" -General endpoint compatibility tests. - -Endpoints covered: - GET /api/v1/difficulty-adjustment - GET /api/v1/prices - GET /api/v1/historical-price - GET /api/v1/historical-price?timestamp=… -""" - -import pytest - -from conftest import show, assert_same_structure - - -DIFFICULTY_KEYS = [ - "progressPercent", "difficultyChange", "estimatedRetargetDate", - "remainingBlocks", "remainingTime", "previousRetarget", - "previousTime", "nextRetargetHeight", "timeAvg", - "adjustedTimeAvg", "timeOffset", "expectedBlocks", -] - - -# ── /api/v1/difficulty-adjustment ──────────────────────────────────── - - -def test_difficulty_adjustment(brk, mempool): - """Difficulty adjustment must have the same structure.""" - path = "/api/v1/difficulty-adjustment" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - for key in DIFFICULTY_KEYS: - assert key in b, f"brk missing '{key}'" - - -def test_difficulty_adjustment_values_sane(brk, mempool): - """Progress must be 0–100 %, remaining blocks must be 0–2016.""" - path = "/api/v1/difficulty-adjustment" - for label, client in [("brk", brk), ("mempool", mempool)]: - d = client.get_json(path) - assert 0 <= d["progressPercent"] <= 100, ( - f"{label} progressPercent out of range: {d['progressPercent']}" - ) - assert 0 <= d["remainingBlocks"] <= 2016, ( - f"{label} remainingBlocks out of range: {d['remainingBlocks']}" - ) - - -# ── /api/v1/prices ─────────────────────────────────────────────────── - - -def test_prices(brk, mempool): - """Current price must have the same structure.""" - path = "/api/v1/prices" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - assert "USD" in b - assert "time" in b - - -def test_prices_positive(brk, mempool): - """USD price must be a positive number on both servers.""" - path = "/api/v1/prices" - for label, client in [("brk", brk), ("mempool", mempool)]: - d = client.get_json(path) - assert d["USD"] > 0, f"{label} USD price is not positive: {d['USD']}" - - -# ── /api/v1/historical-price ───────────────────────────────────────── - - -def test_historical_price(brk, mempool): - """Historical price must have the same structure.""" - path = "/api/v1/historical-price" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m, max_lines=15) - assert_same_structure(b, m) - assert "prices" in b - assert isinstance(b["prices"], list) - - -def test_historical_price_at_block_timestamps(brk, mempool, live): - """Historical price at each discovered block's timestamp must match structure.""" - for block in live.blocks: - info = brk.get_json(f"/api/block/{block.hash}") - ts = info["timestamp"] - path = f"/api/v1/historical-price?timestamp={ts}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - assert "prices" in b - assert len(b["prices"]) > 0 - - -# Well-known timestamps from different eras -HISTORICAL_TIMESTAMPS = [ - 1231006505, # genesis block (2009-01-03) - 1354116278, # block 210000 — first halving (2012-11-28) - 1468082773, # block 420000 — second halving (2016-07-09) - 1588788036, # block 630000 — third halving (2020-05-11) - 1713571767, # block 840000 — fourth halving (2024-04-20) -] - - -@pytest.mark.parametrize("ts", HISTORICAL_TIMESTAMPS, ids=[ - "genesis", "halving1", "halving2", "halving3", "halving4", -]) -def test_historical_price_at_era(brk, mempool, ts): - """Historical price at well-known timestamps must match structure.""" - path = f"/api/v1/historical-price?timestamp={ts}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - assert "prices" in b - assert len(b["prices"]) > 0 diff --git a/scripts/mempool_compat/test_mempool.py b/scripts/mempool_compat/test_mempool.py deleted file mode 100644 index 6cdb14cf8..000000000 --- a/scripts/mempool_compat/test_mempool.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Mempool endpoint compatibility tests. - -Endpoints covered: - GET /api/mempool - GET /api/mempool/txids - GET /api/mempool/recent -""" - -from conftest import show, assert_same_structure - - -# ── /api/mempool ───────────────────────────────────────────────────── - - -def test_mempool_info(brk, mempool): - """Mempool stats must have the same keys and types.""" - path = "/api/mempool" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m, max_lines=15) - assert_same_structure(b, m) - assert isinstance(b["count"], int) - assert isinstance(b["vsize"], int) - - -def test_mempool_info_positive(brk, mempool): - """Both servers must report a non-empty mempool.""" - path = "/api/mempool" - for label, client in [("brk", brk), ("mempool", mempool)]: - d = client.get_json(path) - assert d["count"] > 0, f"{label} mempool count is 0" - assert d["vsize"] > 0, f"{label} mempool vsize is 0" - - -# ── /api/mempool/txids ─────────────────────────────────────────────── - - -def test_mempool_txids(brk, mempool): - """Txid list must be a non-empty array of strings.""" - path = "/api/mempool/txids" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} txids)", f"({len(m)} txids)") - assert isinstance(b, list) and isinstance(m, list) - assert len(b) > 0, "brk mempool has no txids" - assert isinstance(b[0], str) and len(b[0]) == 64 - - -# ── /api/mempool/recent ────────────────────────────────────────────── - - -def test_mempool_recent(brk, mempool): - """Recent mempool txs must have the same element structure.""" - path = "/api/mempool/recent" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert isinstance(b, list) and isinstance(m, list) - assert len(b) > 0 - if b and m: - assert_same_structure(b[0], m[0]) - - -def test_mempool_recent_fields(brk, mempool): - """Each recent tx must have txid, fee, vsize, value.""" - path = "/api/mempool/recent" - for label, client in [("brk", brk), ("mempool", mempool)]: - txs = client.get_json(path) - for tx in txs[:3]: - for key in ["txid", "fee", "vsize", "value"]: - assert key in tx, f"{label} recent tx missing '{key}': {tx}" diff --git a/scripts/mempool_compat/test_mining.py b/scripts/mempool_compat/test_mining.py deleted file mode 100644 index b163f71db..000000000 --- a/scripts/mempool_compat/test_mining.py +++ /dev/null @@ -1,239 +0,0 @@ -""" -Mining endpoint compatibility tests. - -Endpoints covered: - GET /api/v1/mining/pools - GET /api/v1/mining/pools/{period} - GET /api/v1/mining/pool/{slug} - GET /api/v1/mining/pool/{slug}/hashrate - GET /api/v1/mining/pool/{slug}/blocks - GET /api/v1/mining/pool/{slug}/blocks/{height} - GET /api/v1/mining/hashrate/{period} - GET /api/v1/mining/hashrate/pools/{period} - GET /api/v1/mining/difficulty-adjustments/{period} - GET /api/v1/mining/reward-stats/{block_count} - GET /api/v1/mining/blocks/fees/{period} - GET /api/v1/mining/blocks/rewards/{period} - GET /api/v1/mining/blocks/fee-rates/{period} - GET /api/v1/mining/blocks/sizes-weights/{period} - GET /api/v1/mining/blocks/timestamp/{timestamp} -""" - -import pytest - -from conftest import show, assert_same_structure - - -@pytest.fixture(scope="module") -def pool_slugs(mempool): - """Discover the top 3 active pool slugs from the last week.""" - data = mempool.get_json("/api/v1/mining/pools/1w") - pools = data.get("pools", []) if isinstance(data, dict) else [] - slugs = [p["slug"] for p in pools if p.get("blockCount", 0) > 0][:3] - return slugs or ["foundryusa"] - - -@pytest.fixture(scope="module") -def pool_slug(pool_slugs): - return pool_slugs[0] - - -# ── /api/v1/mining/pools ───────────────────────────────────────────── - - -def test_mining_pools_list(brk, mempool): - """Pool list must have the same element structure.""" - path = "/api/v1/mining/pools" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b[:3] if isinstance(b, list) else b, m[:3] if isinstance(m, list) else m) - assert_same_structure(b, m) - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y", "all"]) -def test_mining_pools_by_period(brk, mempool, period): - """Pool stats for a time period must have the same structure.""" - path = f"/api/v1/mining/pools/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/pool/{slug} ─────────────────────────────────────── - - -def test_mining_pool_detail(brk, mempool, pool_slugs): - """Pool detail must have the same structure for top pools.""" - for slug in pool_slugs: - path = f"/api/v1/mining/pool/{slug}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -def test_mining_pool_hashrate(brk, mempool, pool_slugs): - """Pool hashrate history must have the same structure for top pools.""" - for slug in pool_slugs: - path = f"/api/v1/mining/pool/{slug}/hashrate" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -def test_mining_pool_blocks(brk, mempool, pool_slugs): - """Recent blocks by pool must have the same element structure.""" - for slug in pool_slugs: - path = f"/api/v1/mining/pool/{slug}/blocks" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") - assert isinstance(b, list) and isinstance(m, list) - if b and m: - assert_same_structure(b[0], m[0]) - - -def test_mining_pool_blocks_at_height(brk, mempool, pool_slug, live): - """Pool blocks before various heights must have the same element structure.""" - for block in live.blocks[::2]: # every other block - path = f"/api/v1/mining/pool/{pool_slug}/blocks/{block.height}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)") - assert isinstance(b, list) and isinstance(m, list) - if b and m: - assert_same_structure(b[0], m[0]) - - -# ── /api/v1/mining/hashrate ────────────────────────────────────────── - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y"]) -def test_mining_hashrate(brk, mempool, period): - """Network hashrate + difficulty must have the same structure.""" - path = f"/api/v1/mining/hashrate/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/hashrate/pools ──────────────────────────────────── - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "1y"]) -def test_mining_hashrate_pools(brk, mempool, period): - """Per-pool hashrate must have the same structure.""" - path = f"/api/v1/mining/hashrate/pools/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/difficulty-adjustments ───────────────────────────── - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y"]) -def test_mining_difficulty_adjustments(brk, mempool, period): - """Historical difficulty adjustments must have the same structure.""" - path = f"/api/v1/mining/difficulty-adjustments/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/reward-stats ────────────────────────────────────── - - -@pytest.mark.parametrize("block_count", [10, 100, 500]) -def test_mining_reward_stats(brk, mempool, block_count): - """Reward stats must have the same structure.""" - path = f"/api/v1/mining/reward-stats/{block_count}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - - -# ── /api/v1/mining/blocks/fees ─────────────────────────────────────── - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) -def test_mining_blocks_fees(brk, mempool, period): - """Average block fees must have the same element structure.""" - path = f"/api/v1/mining/blocks/fees/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/blocks/rewards ──────────────────────────────────── - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) -def test_mining_blocks_rewards(brk, mempool, period): - """Average block rewards must have the same element structure.""" - path = f"/api/v1/mining/blocks/rewards/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/blocks/fee-rates ────────────────────────────────── - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) -def test_mining_blocks_fee_rates(brk, mempool, period): - """Block fee-rate percentiles must have the same element structure.""" - path = f"/api/v1/mining/blocks/fee-rates/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/blocks/sizes-weights ────────────────────────────── - - -@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"]) -def test_mining_blocks_sizes_weights(brk, mempool, period): - """Block sizes and weights must have the same structure.""" - path = f"/api/v1/mining/blocks/sizes-weights/{period}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, _summary(b), _summary(m)) - assert_same_structure(b, m) - - -# ── /api/v1/mining/blocks/timestamp ────────────────────────────────── - - -def test_mining_blocks_timestamp(brk, mempool, live): - """Block lookup by timestamp must have the same structure for various eras.""" - for block in live.blocks: - # Get the block timestamp from brk - info = brk.get_json(f"/api/block/{block.hash}") - ts = info["timestamp"] - path = f"/api/v1/mining/blocks/timestamp/{ts}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - - -# ── helpers ────────────────────────────────────────────────────────── - - -def _summary(data): - """Short description of a response for the show() call.""" - if isinstance(data, list): - return f"({len(data)} items)" - if isinstance(data, dict): - return f"(keys: {list(data.keys())})" - return str(data) diff --git a/scripts/mempool_compat/test_transactions.py b/scripts/mempool_compat/test_transactions.py deleted file mode 100644 index b2a66ed6e..000000000 --- a/scripts/mempool_compat/test_transactions.py +++ /dev/null @@ -1,161 +0,0 @@ -""" -Transaction endpoint compatibility tests — parametrized across blockchain eras. - -Endpoints covered: - GET /api/tx/{txid} - GET /api/tx/{txid}/hex text/plain - GET /api/tx/{txid}/raw application/octet-stream - GET /api/tx/{txid}/status - GET /api/tx/{txid}/merkle-proof - GET /api/tx/{txid}/merkleblock-proof text/plain - GET /api/tx/{txid}/outspend/{vout} - GET /api/tx/{txid}/outspends - GET /api/v1/cpfp/{txid} - GET /api/v1/transaction-times -""" - -import pytest - -from conftest import show, assert_same_structure, assert_same_values - - -@pytest.fixture(params=range(8), ids=[ - "h100", "h100k", "h400k", "h630k", "h800k", "recent1k", "recent100", "recent10", -]) -def block(request, live): - i = request.param - if i >= len(live.blocks): - pytest.skip("block not discovered") - return live.blocks[i] - - -# ── /api/tx/{txid} ─────────────────────────────────────────────────── - - -def test_tx_by_id(brk, mempool, block): - """Full transaction data must match for a confirmed tx.""" - path = f"/api/tx/{block.txid}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m, exclude={"sigops"}) - - -def test_tx_coinbase(brk, mempool, block): - """Coinbase transaction must match.""" - path = f"/api/tx/{block.coinbase_txid}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m, exclude={"sigops"}) - - -# ── /api/tx/{txid}/hex ─────────────────────────────────────────────── - - -def test_tx_hex(brk, mempool, block): - """Raw transaction hex must be identical.""" - path = f"/api/tx/{block.txid}/hex" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b[:80] + "…", m[:80] + "…") - assert b == m - - -# ── /api/tx/{txid}/raw ─────────────────────────────────────────────── - - -def test_tx_raw(brk, mempool, block): - """Raw transaction bytes must be identical.""" - path = f"/api/tx/{block.txid}/raw" - b = brk.get_bytes(path) - m = mempool.get_bytes(path) - show("GET", path, f"<{len(b)} bytes>", f"<{len(m)} bytes>") - assert b == m - - -# ── /api/tx/{txid}/status ──────────────────────────────────────────── - - -def test_tx_status(brk, mempool, block): - """Confirmation status must match for a confirmed tx.""" - path = f"/api/tx/{block.txid}/status" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m) - - -# ── /api/tx/{txid}/merkle-proof ────────────────────────────────────── - - -def test_tx_merkle_proof(brk, mempool, block): - """Merkle inclusion proof must match.""" - path = f"/api/tx/{block.txid}/merkle-proof" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m) - - -# ── /api/tx/{txid}/merkleblock-proof ───────────────────────────────── - - -def test_tx_merkleblock_proof(brk, mempool, block): - """BIP37 merkleblock proof hex must be identical.""" - path = f"/api/tx/{block.txid}/merkleblock-proof" - b = brk.get_text(path) - m = mempool.get_text(path) - show("GET", path, b[:80] + "…", m[:80] + "…") - assert b == m - - -# ── /api/tx/{txid}/outspend/{vout} ─────────────────────────────────── - - -def test_tx_outspend(brk, mempool, block): - """Spending status of output 0 must match exactly.""" - path = f"/api/tx/{block.txid}/outspend/0" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m) - - -# ── /api/tx/{txid}/outspends ───────────────────────────────────────── - - -def test_tx_outspends(brk, mempool, block): - """Spending status of all outputs must match exactly.""" - path = f"/api/tx/{block.txid}/outspends" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_values(b, m) - - -# ── /api/v1/cpfp/{txid} ───────────────────────────────────────────── - - -def test_cpfp(brk, mempool, block): - """CPFP info structure must match for a confirmed tx.""" - path = f"/api/v1/cpfp/{block.txid}" - b = brk.get_json(path) - m = mempool.get_json(path) - show("GET", path, b, m) - assert_same_structure(b, m) - - -# ── /api/v1/transaction-times ──────────────────────────────────────── - - -def test_transaction_times(brk, mempool, live): - """First-seen timestamps array must have the same length.""" - txids = [b.txid for b in live.blocks[:3]] - params = [("txId[]", t) for t in txids] - path = "/api/v1/transaction-times" - b = brk.get_json(path, params=params) - m = mempool.get_json(path, params=params) - show("GET", f"{path}?txId[]={{{len(txids)} txids}}", b, m) - assert isinstance(b, list) and isinstance(m, list) - assert len(b) == len(m) == len(txids) diff --git a/scripts/mempool_compat/uv.lock b/scripts/mempool_compat/uv.lock deleted file mode 100644 index 7658fbaf8..000000000 --- a/scripts/mempool_compat/uv.lock +++ /dev/null @@ -1,385 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.9" -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version < '3.10'", -] - -[[package]] -name = "certifi" -version = "2026.2.25" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, - { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, - { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, - { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, - { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, - { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, - { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, - { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, - { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, - { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, - { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, - { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, - { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, - { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, - { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, - { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, - { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, - { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, - { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, - { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, - { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, - { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, - { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/01/1b/ef725f8eb19b5a261b30f78efa9252ef9d017985cb499102f6f49834cd12/charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217", size = 299121, upload-time = "2026-04-02T09:28:14.372Z" }, - { url = "https://files.pythonhosted.org/packages/a3/22/2f12878fbc680fbbb52386cd39a379801f62eaca74fc8b323381325f0f04/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5", size = 200612, upload-time = "2026-04-02T09:28:16.162Z" }, - { url = "https://files.pythonhosted.org/packages/bc/b6/10c84e789126ca97d4a7228863a30481e786980a8b8cfcbf4f30658ca63c/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9", size = 221041, upload-time = "2026-04-02T09:28:17.554Z" }, - { url = "https://files.pythonhosted.org/packages/21/7b/c414866a138400b2e81973d006da7f694cfeaf895ef07d2cba9a8743841a/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a", size = 216323, upload-time = "2026-04-02T09:28:18.863Z" }, - { url = "https://files.pythonhosted.org/packages/2e/92/bdcf94997e06b223d826df3abed45a5ad6e17f609b7df9d25cd23b5bde30/charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc", size = 208419, upload-time = "2026-04-02T09:28:20.332Z" }, - { url = "https://files.pythonhosted.org/packages/1a/64/3f9142293c88b1b10e199649ed1330f070c2a68e305335a5819fa7f25fa7/charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00", size = 195016, upload-time = "2026-04-02T09:28:21.657Z" }, - { url = "https://files.pythonhosted.org/packages/c1/d1/d8a6b7dd5c5636b76ce0d080bc57d8e56c7bbd6bc2ac941529a35e41d84a/charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776", size = 206115, upload-time = "2026-04-02T09:28:23.259Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8c/60ebe912379627d023eb96995b40bc50308729f210f43d66109ca0a7bbd2/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319", size = 204022, upload-time = "2026-04-02T09:28:24.779Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2a/41816ceda78a551cbfdfbeab6f3891152b0e3f758ce6580c2c18c829f774/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24", size = 195914, upload-time = "2026-04-02T09:28:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9b/7c7f4b7f11525fcbdfba752455314ac60646bae91cdd671d531c1f7a97c6/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42", size = 222159, upload-time = "2026-04-02T09:28:27.504Z" }, - { url = "https://files.pythonhosted.org/packages/9f/57/301682e7469bdbfa2ce219a804f0668b2266ab8520570d85d3b3ef483ea3/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4", size = 206154, upload-time = "2026-04-02T09:28:28.848Z" }, - { url = "https://files.pythonhosted.org/packages/20/ec/90339ff5cdc598b265748c1f231c7d7fbd9123a92cee10f757e0b1448de4/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67", size = 217423, upload-time = "2026-04-02T09:28:30.248Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e7/a7a6147f8e3375676309cf584b25c72a3bab784ea4085b0011fa07b23aeb/charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274", size = 210604, upload-time = "2026-04-02T09:28:31.736Z" }, - { url = "https://files.pythonhosted.org/packages/1a/62/d9340c7a79c393e57807d7fb6c57e82060687891f81b74d3201958b919c1/charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366", size = 144631, upload-time = "2026-04-02T09:28:33.158Z" }, - { url = "https://files.pythonhosted.org/packages/21/e7/92901117e2ddc8facfe8235a3ecd4eb482185b2ad5d5b6606b37c1afea06/charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444", size = 154710, upload-time = "2026-04-02T09:28:34.557Z" }, - { url = "https://files.pythonhosted.org/packages/cc/4f/e1fb138201ad9a32499dd9a98aa4a5a5441fbf7f56b52b619a54b7ee8777/charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c", size = 143716, upload-time = "2026-04-02T09:28:35.908Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "idna" -version = "3.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "mempool-compat" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "requests", version = "2.32.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "requests", version = "2.33.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, -] - -[package.metadata] -requires-dist = [ - { name = "pytest", specifier = ">=7.0" }, - { name = "requests", specifier = ">=2.28" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, - { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", marker = "python_full_version < '3.10'" }, - { name = "pluggy", marker = "python_full_version < '3.10'" }, - { name = "pygments", marker = "python_full_version < '3.10'" }, - { name = "tomli", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, - { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "packaging", marker = "python_full_version >= '3.10'" }, - { name = "pluggy", marker = "python_full_version >= '3.10'" }, - { name = "pygments", marker = "python_full_version >= '3.10'" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, -] - -[[package]] -name = "requests" -version = "2.32.5" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "certifi", marker = "python_full_version < '3.10'" }, - { name = "charset-normalizer", marker = "python_full_version < '3.10'" }, - { name = "idna", marker = "python_full_version < '3.10'" }, - { name = "urllib3", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, -] - -[[package]] -name = "requests" -version = "2.33.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -dependencies = [ - { name = "certifi", marker = "python_full_version >= '3.10'" }, - { name = "charset-normalizer", marker = "python_full_version >= '3.10'" }, - { name = "idna", marker = "python_full_version >= '3.10'" }, - { name = "urllib3", marker = "python_full_version >= '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, -] - -[[package]] -name = "tomli" -version = "2.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, - { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, - { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, - { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, - { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, - { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, - { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, - { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, - { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, - { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, - { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, - { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, - { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, - { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, - { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, - { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, - { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, - { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, - { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, - { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, - { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, - { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, - { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, - { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, - { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, - { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, - { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, - { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, - { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, - { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, - { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, - { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, - { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, - { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, - { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, - { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, - { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, - { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "urllib3" -version = "2.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, -] diff --git a/website/scripts/_types.js b/website/scripts/_types.js index a5484dfe7..40eb864c1 100644 --- a/website/scripts/_types.js +++ b/website/scripts/_types.js @@ -7,7 +7,6 @@ * @import { Options } from './options/full.js' * * @import { PersistedValue } from './utils/persisted.js' - * @import { MapCache } from './utils/cache.js' * * @import { SingleValueData, CandlestickData, Series, AnySeries, ISeries, HistogramData, LineData, BaselineData, LineSeriesPartialOptions, BaselineSeriesPartialOptions, HistogramSeriesPartialOptions, CandlestickSeriesPartialOptions, Chart, Legend } from "./utils/chart/index.js" * diff --git a/website/scripts/explorer/address.js b/website/scripts/explorer/address.js index c9e857745..f9790666f 100644 --- a/website/scripts/explorer/address.js +++ b/website/scripts/explorer/address.js @@ -1,5 +1,4 @@ import { brk } from "../utils/client.js"; -import { createMapCache } from "../utils/cache.js"; import { latestPrice } from "../utils/price.js"; import { createRow, formatBtc, renderTx, showPanel, hidePanel, TX_PAGE_SIZE } from "./render.js"; @@ -8,9 +7,6 @@ import { createRow, formatBtc, renderTx, showPanel, hidePanel, TX_PAGE_SIZE } fr /** @type {HTMLDivElement} */ let txSection; /** @type {string} */ let currentAddr = ""; -const statsCache = createMapCache(50); -const txCache = createMapCache(200); - const ROW_LABELS = [ "Address", "Confirmed Balance", @@ -63,9 +59,7 @@ export async function update(address, signal) { while (txSection.children.length > 1) txSection.lastChild?.remove(); try { - const stats = await statsCache.fetch(address, () => - brk.getAddress(address, { signal }), - ); + const stats = await brk.getAddress(address, { signal }); if (signal.aborted || currentAddr !== address) return; const chain = stats.chainStats; @@ -118,11 +112,8 @@ export async function update(address, signal) { async function loadMore() { if (currentAddr !== address) return; loading = true; - const key = `${address}:${pageIndex}`; try { - const txs = await txCache.fetch(key, () => - brk.getAddressTxs(address, afterTxid, { signal }), - ); + const txs = await brk.getAddressTxs(address, afterTxid, { signal }); if (currentAddr !== address) return; for (const tx of txs) txSection.append(renderTx(tx)); pageIndex++; diff --git a/website/scripts/explorer/block.js b/website/scripts/explorer/block.js index 6e7c0f559..0363ce841 100644 --- a/website/scripts/explorer/block.js +++ b/website/scripts/explorer/block.js @@ -1,5 +1,4 @@ import { brk } from "../utils/client.js"; -import { createMapCache } from "../utils/cache.js"; import { createPersistedValue } from "../utils/persisted.js"; import { createRow, formatFeeRate, formatHeightPrefix, renderTx, showPanel, hidePanel, TX_PAGE_SIZE } from "./render.js"; @@ -68,7 +67,6 @@ const ROW_DEFS = [ let txTotalPages = 0; let txLoading = false; let txLoaded = false; -const txPageCache = createMapCache(200); const txPageParam = createPersistedValue({ defaultValue: 0, @@ -200,11 +198,8 @@ async function loadTxPage(page, pushUrl = true) { txLoaded = true; if (pushUrl) txPageParam.setImmediate(page); updateTxNavs(page); - const key = `${block.id}:${page}`; try { - const txs = await txPageCache.fetch(key, () => - brk.getBlockTxsFromIndex(block.id, page * TX_PAGE_SIZE), - ); + const txs = await brk.getBlockTxsFromIndex(block.id, page * TX_PAGE_SIZE); txList.innerHTML = ""; const ascii = block.extras.coinbaseSignatureAscii; for (const tx of txs) txList.append(renderTx(tx, ascii)); diff --git a/website/scripts/explorer/index.js b/website/scripts/explorer/index.js index 2666cb32a..122463b9e 100644 --- a/website/scripts/explorer/index.js +++ b/website/scripts/explorer/index.js @@ -1,6 +1,5 @@ import { explorerElement } from "../utils/elements.js"; import { brk } from "../utils/client.js"; -import { createMapCache } from "../utils/cache.js"; import { initChain, goToCube, @@ -30,7 +29,6 @@ import { /** @type {number | undefined} */ let pollInterval; let navController = new AbortController(); -const txCache = createMapCache(50); let lastLoadedUrl = ""; function navigate() { @@ -134,7 +132,7 @@ async function load() { if (kind === "tx" && value) { const txid = await resolveTxid(value, { signal }); if (signal.aborted) return; - const tx = await txCache.fetch(txid, () => brk.getTx(txid, { signal })); + const tx = await brk.getTx(txid, { signal }); if (signal.aborted) return; await goToCube(tx.status?.blockHash ?? tx.status?.blockHeight ?? null, { silent: true }); updateTx(tx); @@ -178,7 +176,7 @@ async function navigateToTx(txidOrIndex) { try { const txid = await resolveTxid(txidOrIndex, { signal }); if (signal.aborted) return; - const tx = await txCache.fetch(txid, () => brk.getTx(txid, { signal })); + const tx = await brk.getTx(txid, { signal }); if (signal.aborted) return; await goToCube(tx.status?.blockHash ?? tx.status?.blockHeight ?? null, { silent: true }); updateTx(tx); diff --git a/website/scripts/utils/cache.js b/website/scripts/utils/cache.js deleted file mode 100644 index 743e91ba6..000000000 --- a/website/scripts/utils/cache.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @template V - * @param {number} [maxSize] - */ -export function createMapCache(maxSize = 100) { - /** @type {Map} */ - const map = new Map(); - - /** @param {string} key @param {V} value */ - const set = (key, value) => { - if (map.size >= maxSize && !map.has(key)) { - const first = map.keys().next().value; - if (first !== undefined) map.delete(first); - } - map.set(key, value); - }; - - return { - /** @param {string} key @returns {V | undefined} */ - get: (key) => map.get(key), - /** @param {string} key @returns {boolean} */ - has: (key) => map.has(key), - set, - /** @param {string} key @param {() => Promise} fetcher @returns {Promise} */ - async fetch(key, fetcher) { - const hit = map.get(key); - if (hit !== undefined) return hit; - const value = await fetcher(); - set(key, value); - return value; - }, - }; -} - -/** - * @template V - * @typedef {ReturnType>} MapCache - */ diff --git a/website/scripts/utils/chart/index.js b/website/scripts/utils/chart/index.js index 9ea8bfa9d..e20f9882d 100644 --- a/website/scripts/utils/chart/index.js +++ b/website/scripts/utils/chart/index.js @@ -10,7 +10,6 @@ import { capture } from "./capture.js"; import { colors } from "../colors.js"; import { createRadios, createSelect, getElementById } from "../dom.js"; import { createPersistedValue } from "../persisted.js"; -import { createMapCache } from "../cache.js"; import { onChange as onThemeChange } from "../theme.js"; import { throttle, debounce } from "../timing.js"; import { serdeBool, INDEX_FROM_LABEL } from "../serde.js"; @@ -193,7 +192,8 @@ export function createChart({ parent, brk, fitContent }) { }, }; - const cache = createMapCache(Infinity); + /** @type {Map} */ + const cache = new Map(); // Range state: localStorage stores all ranges per-index, URL stores current range only /** @typedef {{ from: number, to: number }} Range */