mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-19 23:18:10 -07:00
global: opreturn part 4
This commit is contained in:
@@ -2291,6 +2291,28 @@ impl CapLossMvrvPriceProfitPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CarrierDataOutputPostPattern {
|
||||
pub carrier_tx_count: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
pub carrier_vsize: AverageBlockCumulativeSumPattern<VSize>,
|
||||
pub data_share: BpsPercentRatioPattern2,
|
||||
pub output_count: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
pub post_op_return_bytes: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
}
|
||||
|
||||
impl CarrierDataOutputPostPattern {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
carrier_tx_count: AverageBlockCumulativeSumPattern::new(client.clone(), _m(&acc, "carrier_tx_count")),
|
||||
carrier_vsize: AverageBlockCumulativeSumPattern::new(client.clone(), _m(&acc, "carrier_vsize")),
|
||||
data_share: BpsPercentRatioPattern2::new(client.clone(), _m(&acc, "data_share")),
|
||||
output_count: AverageBlockCumulativeSumPattern::new(client.clone(), _m(&acc, "output_count")),
|
||||
post_op_return_bytes: AverageBlockCumulativeSumPattern::new(client.clone(), _m(&acc, "post_op_return_bytes")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct CentsToUsdPattern4 {
|
||||
pub cents: SeriesPattern1<Cents>,
|
||||
@@ -5388,6 +5410,7 @@ pub struct SeriesTree_OpReturn_Total {
|
||||
pub post_op_return_bytes: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
pub carrier_tx_count: AverageBlockCumulativeSumPattern<StoredU64>,
|
||||
pub carrier_vsize: AverageBlockCumulativeSumPattern<VSize>,
|
||||
pub data_share: BpsPercentRatioPattern2,
|
||||
}
|
||||
|
||||
impl SeriesTree_OpReturn_Total {
|
||||
@@ -5396,6 +5419,7 @@ impl SeriesTree_OpReturn_Total {
|
||||
post_op_return_bytes: AverageBlockCumulativeSumPattern::new(client.clone(), "op_return_post_op_return_bytes".to_string()),
|
||||
carrier_tx_count: AverageBlockCumulativeSumPattern::new(client.clone(), "op_return_carrier_tx_count".to_string()),
|
||||
carrier_vsize: AverageBlockCumulativeSumPattern::new(client.clone(), "op_return_carrier_vsize".to_string()),
|
||||
data_share: BpsPercentRatioPattern2::new(client.clone(), "op_return_data_share".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5459,19 +5483,19 @@ impl SeriesTree_OpReturn_ByKind {
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_OpReturn_Policy {
|
||||
pub standard: CarrierOutputPostPattern,
|
||||
pub oversized: CarrierOutputPostPattern,
|
||||
pub multiple: CarrierOutputPostPattern,
|
||||
pub pre_v30_nonstandard: CarrierOutputPostPattern,
|
||||
pub standard: CarrierDataOutputPostPattern,
|
||||
pub oversized: CarrierDataOutputPostPattern,
|
||||
pub multiple: CarrierDataOutputPostPattern,
|
||||
pub pre_v30_nonstandard: CarrierDataOutputPostPattern,
|
||||
}
|
||||
|
||||
impl SeriesTree_OpReturn_Policy {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
standard: CarrierOutputPostPattern::new(client.clone(), "op_return_policy_standard".to_string()),
|
||||
oversized: CarrierOutputPostPattern::new(client.clone(), "op_return_policy_oversized".to_string()),
|
||||
multiple: CarrierOutputPostPattern::new(client.clone(), "op_return_policy_multiple".to_string()),
|
||||
pre_v30_nonstandard: CarrierOutputPostPattern::new(client.clone(), "op_return_policy_pre_v30_nonstandard".to_string()),
|
||||
standard: CarrierDataOutputPostPattern::new(client.clone(), "op_return_policy_standard".to_string()),
|
||||
oversized: CarrierDataOutputPostPattern::new(client.clone(), "op_return_policy_oversized".to_string()),
|
||||
multiple: CarrierDataOutputPostPattern::new(client.clone(), "op_return_policy_multiple".to_string()),
|
||||
pre_v30_nonstandard: CarrierDataOutputPostPattern::new(client.clone(), "op_return_policy_pre_v30_nonstandard".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ impl Computer {
|
||||
|
||||
let op_return = scope.spawn(|| {
|
||||
timed("Computed OP_RETURN", || {
|
||||
self.op_return.compute(indexer, exit)
|
||||
self.op_return.compute(indexer, &self.blocks, exit)
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_types::{OpReturnKind, VSize};
|
||||
use brk_types::{BasisPoints16, Height, OpReturnKind, StoredU64, VSize};
|
||||
use vecdb::{AnyVec, Exit, ReadableVec, VecIndex};
|
||||
|
||||
use super::{Vecs, vecs::Totals};
|
||||
use crate::{
|
||||
blocks,
|
||||
internal::{PercentPerBlock, RatioU64Bp16},
|
||||
};
|
||||
|
||||
const KIND_COUNT: usize = OpReturnKind::Unknown as usize + 1;
|
||||
const OLD_STANDARD_MAX_POST_OP_RETURN_BYTES: u64 = 82;
|
||||
@@ -40,7 +44,12 @@ impl Carrier {
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn compute(&mut self, indexer: &Indexer, exit: &Exit) -> Result<()> {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
indexer: &Indexer,
|
||||
blocks: &blocks::Vecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.db.sync_bg_tasks()?;
|
||||
|
||||
let starting_lengths = indexer.safe_lengths();
|
||||
@@ -132,6 +141,23 @@ impl Vecs {
|
||||
}
|
||||
|
||||
self.compute_cumulative(starting_lengths.height, exit)?;
|
||||
let block_size = &blocks.size.size.cumulative.height;
|
||||
compute_data_share(
|
||||
starting_lengths.height,
|
||||
&mut self.total.data_share,
|
||||
&self.total.metrics.post_op_return_bytes.cumulative.height,
|
||||
block_size,
|
||||
exit,
|
||||
)?;
|
||||
for policy in self.policy.iter_mut() {
|
||||
compute_data_share(
|
||||
starting_lengths.height,
|
||||
&mut policy.data_share,
|
||||
&policy.metrics.total.post_op_return_bytes.cumulative.height,
|
||||
block_size,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
let exit = exit.clone();
|
||||
self.db.run_bg(move |db| {
|
||||
@@ -142,6 +168,16 @@ impl Vecs {
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_data_share(
|
||||
max_from: Height,
|
||||
target: &mut PercentPerBlock<BasisPoints16>,
|
||||
data: &impl ReadableVec<Height, StoredU64>,
|
||||
block_size: &impl ReadableVec<Height, StoredU64>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
target.compute_binary::<StoredU64, StoredU64, RatioU64Bp16>(max_from, data, block_size, exit)
|
||||
}
|
||||
|
||||
fn finalize_transaction(
|
||||
total: &mut Totals,
|
||||
by_kind: &mut [Totals; KIND_COUNT],
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::path::Path;
|
||||
use brk_error::Result;
|
||||
use brk_types::Version;
|
||||
|
||||
use super::{ByKind, Metrics, Policy, TotalMetrics, Vecs};
|
||||
use super::{ByKind, Metrics, Policy, Total, Vecs};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
@@ -20,7 +20,7 @@ impl Vecs {
|
||||
cached_starts: &Windows<&WindowStartVec>,
|
||||
) -> Result<Self> {
|
||||
let db = open_db(parent_path, super::DB_NAME, 1_000_000)?;
|
||||
let total = TotalMetrics::forced_import(&db, "op_return", version, indexes, cached_starts)?;
|
||||
let total = Total::forced_import(&db, "op_return", version, indexes, cached_starts)?;
|
||||
let by_kind = ByKind::try_new(|_, name| {
|
||||
Metrics::forced_import(
|
||||
&db,
|
||||
|
||||
@@ -4,6 +4,6 @@ mod import;
|
||||
mod vecs;
|
||||
|
||||
pub use by_kind::ByKind;
|
||||
pub use vecs::{Metrics, Policy, TotalMetrics, Vecs};
|
||||
pub use vecs::{Metrics, Policy, Total, Vecs};
|
||||
|
||||
pub const DB_NAME: &str = "op_return";
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredU64, VSize, Version};
|
||||
use brk_types::{BasisPoints16, Height, StoredU64, VSize, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use super::ByKind;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{PerBlockCumulativeRolling, WindowStartVec, Windows},
|
||||
internal::{PerBlockCumulativeRolling, PercentPerBlock, WindowStartVec, Windows},
|
||||
};
|
||||
|
||||
pub type Series<T, M = Rw> = PerBlockCumulativeRolling<T, T, M>;
|
||||
@@ -112,6 +113,35 @@ impl TotalMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct Total<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub metrics: TotalMetrics<M>,
|
||||
pub data_share: PercentPerBlock<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
impl Total {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
prefix: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &Windows<&WindowStartVec>,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
metrics: TotalMetrics::forced_import(db, prefix, version, indexes, cached_starts)?,
|
||||
data_share: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{prefix}_data_share"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Metrics<M: StorageMode = Rw> {
|
||||
pub output_count: Series<StoredU64, M>,
|
||||
@@ -175,12 +205,41 @@ impl Metrics {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct PolicyMetrics<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub metrics: Metrics<M>,
|
||||
pub data_share: PercentPerBlock<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
impl PolicyMetrics {
|
||||
fn forced_import(
|
||||
db: &Database,
|
||||
prefix: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &Windows<&WindowStartVec>,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
metrics: Metrics::forced_import(db, prefix, version, indexes, cached_starts)?,
|
||||
data_share: PercentPerBlock::forced_import(
|
||||
db,
|
||||
&format!("{prefix}_data_share"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Policy<M: StorageMode = Rw> {
|
||||
pub standard: Metrics<M>,
|
||||
pub oversized: Metrics<M>,
|
||||
pub multiple: Metrics<M>,
|
||||
pub pre_v30_nonstandard: Metrics<M>,
|
||||
pub standard: PolicyMetrics<M>,
|
||||
pub oversized: PolicyMetrics<M>,
|
||||
pub multiple: PolicyMetrics<M>,
|
||||
pub pre_v30_nonstandard: PolicyMetrics<M>,
|
||||
}
|
||||
|
||||
impl Policy {
|
||||
@@ -191,7 +250,7 @@ impl Policy {
|
||||
cached_starts: &Windows<&WindowStartVec>,
|
||||
) -> Result<Self> {
|
||||
let import = |name| {
|
||||
Metrics::forced_import(
|
||||
PolicyMetrics::forced_import(
|
||||
db,
|
||||
&format!("op_return_policy_{name}"),
|
||||
version,
|
||||
@@ -208,7 +267,7 @@ impl Policy {
|
||||
})
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = &Metrics> {
|
||||
fn iter(&self) -> impl Iterator<Item = &PolicyMetrics> {
|
||||
[
|
||||
&self.standard,
|
||||
&self.oversized,
|
||||
@@ -218,7 +277,7 @@ impl Policy {
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> impl Iterator<Item = &mut Metrics> {
|
||||
pub(super) fn iter_mut(&mut self) -> impl Iterator<Item = &mut PolicyMetrics> {
|
||||
[
|
||||
&mut self.standard,
|
||||
&mut self.oversized,
|
||||
@@ -233,18 +292,22 @@ impl Policy {
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
#[traversable(skip)]
|
||||
pub(crate) db: Database,
|
||||
pub total: TotalMetrics<M>,
|
||||
pub total: Total<M>,
|
||||
pub by_kind: ByKind<Metrics<M>>,
|
||||
pub policy: Policy<M>,
|
||||
}
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn min_len(&self) -> usize {
|
||||
self.by_kind
|
||||
let len = self
|
||||
.by_kind
|
||||
.iter()
|
||||
.chain(self.policy.iter())
|
||||
.map(Metrics::len)
|
||||
.fold(self.total.len(), usize::min)
|
||||
.fold(self.total.len(), usize::min);
|
||||
self.policy
|
||||
.iter()
|
||||
.map(|metrics| metrics.len())
|
||||
.fold(len, usize::min)
|
||||
}
|
||||
|
||||
pub(crate) fn validate_and_truncate(&mut self, version: Version, height: Height) -> Result<()> {
|
||||
|
||||
@@ -3820,6 +3820,31 @@ function createCapLossMvrvPriceProfitPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CarrierDataOutputPostPattern
|
||||
* @property {AverageBlockCumulativeSumPattern<StoredU64>} carrierTxCount
|
||||
* @property {AverageBlockCumulativeSumPattern<VSize>} carrierVsize
|
||||
* @property {BpsPercentRatioPattern2} dataShare
|
||||
* @property {AverageBlockCumulativeSumPattern<StoredU64>} outputCount
|
||||
* @property {AverageBlockCumulativeSumPattern<StoredU64>} postOpReturnBytes
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a CarrierDataOutputPostPattern pattern node
|
||||
* @param {BrkClient} client
|
||||
* @param {string} acc - Accumulated series name
|
||||
* @returns {CarrierDataOutputPostPattern}
|
||||
*/
|
||||
function createCarrierDataOutputPostPattern(client, acc) {
|
||||
return {
|
||||
carrierTxCount: createAverageBlockCumulativeSumPattern(client, _m(acc, 'carrier_tx_count')),
|
||||
carrierVsize: createAverageBlockCumulativeSumPattern(client, _m(acc, 'carrier_vsize')),
|
||||
dataShare: createBpsPercentRatioPattern2(client, _m(acc, 'data_share')),
|
||||
outputCount: createAverageBlockCumulativeSumPattern(client, _m(acc, 'output_count')),
|
||||
postOpReturnBytes: createAverageBlockCumulativeSumPattern(client, _m(acc, 'post_op_return_bytes')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} CentsToUsdPattern4
|
||||
* @property {SeriesPattern1<Cents>} cents
|
||||
@@ -6192,6 +6217,7 @@ function createTransferPattern(client, acc) {
|
||||
* @property {AverageBlockCumulativeSumPattern<StoredU64>} postOpReturnBytes
|
||||
* @property {AverageBlockCumulativeSumPattern<StoredU64>} carrierTxCount
|
||||
* @property {AverageBlockCumulativeSumPattern<VSize>} carrierVsize
|
||||
* @property {BpsPercentRatioPattern2} dataShare
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -6223,10 +6249,10 @@ function createTransferPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} SeriesTree_OpReturn_Policy
|
||||
* @property {CarrierOutputPostPattern} standard
|
||||
* @property {CarrierOutputPostPattern} oversized
|
||||
* @property {CarrierOutputPostPattern} multiple
|
||||
* @property {CarrierOutputPostPattern} preV30Nonstandard
|
||||
* @property {CarrierDataOutputPostPattern} standard
|
||||
* @property {CarrierDataOutputPostPattern} oversized
|
||||
* @property {CarrierDataOutputPostPattern} multiple
|
||||
* @property {CarrierDataOutputPostPattern} preV30Nonstandard
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -9988,6 +10014,7 @@ class BrkClient extends BrkClientBase {
|
||||
postOpReturnBytes: createAverageBlockCumulativeSumPattern(this, 'op_return_post_op_return_bytes'),
|
||||
carrierTxCount: createAverageBlockCumulativeSumPattern(this, 'op_return_carrier_tx_count'),
|
||||
carrierVsize: createAverageBlockCumulativeSumPattern(this, 'op_return_carrier_vsize'),
|
||||
dataShare: createBpsPercentRatioPattern2(this, 'op_return_data_share'),
|
||||
},
|
||||
byKind: {
|
||||
runes: createCarrierOutputPostPattern(this, 'op_return_runes'),
|
||||
@@ -10015,10 +10042,10 @@ class BrkClient extends BrkClientBase {
|
||||
unknown: createCarrierOutputPostPattern(this, 'op_return_unknown'),
|
||||
},
|
||||
policy: {
|
||||
standard: createCarrierOutputPostPattern(this, 'op_return_policy_standard'),
|
||||
oversized: createCarrierOutputPostPattern(this, 'op_return_policy_oversized'),
|
||||
multiple: createCarrierOutputPostPattern(this, 'op_return_policy_multiple'),
|
||||
preV30Nonstandard: createCarrierOutputPostPattern(this, 'op_return_policy_pre_v30_nonstandard'),
|
||||
standard: createCarrierDataOutputPostPattern(this, 'op_return_policy_standard'),
|
||||
oversized: createCarrierDataOutputPostPattern(this, 'op_return_policy_oversized'),
|
||||
multiple: createCarrierDataOutputPostPattern(this, 'op_return_policy_multiple'),
|
||||
preV30Nonstandard: createCarrierDataOutputPostPattern(this, 'op_return_policy_pre_v30_nonstandard'),
|
||||
},
|
||||
},
|
||||
mining: {
|
||||
|
||||
@@ -3577,6 +3577,17 @@ class CapLossMvrvPriceProfitPattern:
|
||||
self.price: BpsCentsRatioSatsUsdPattern = BpsCentsRatioSatsUsdPattern(client, _m(acc, 'realized_price'))
|
||||
self.profit: BlockCumulativeSumPattern = BlockCumulativeSumPattern(client, _m(acc, 'realized_profit'))
|
||||
|
||||
class CarrierDataOutputPostPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClient, acc: str):
|
||||
"""Create pattern node with accumulated series name."""
|
||||
self.carrier_tx_count: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'carrier_tx_count'))
|
||||
self.carrier_vsize: AverageBlockCumulativeSumPattern[VSize] = AverageBlockCumulativeSumPattern(client, _m(acc, 'carrier_vsize'))
|
||||
self.data_share: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, _m(acc, 'data_share'))
|
||||
self.output_count: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'output_count'))
|
||||
self.post_op_return_bytes: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, _m(acc, 'post_op_return_bytes'))
|
||||
|
||||
class CentsToUsdPattern4:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
@@ -5064,6 +5075,7 @@ class SeriesTree_OpReturn_Total:
|
||||
self.post_op_return_bytes: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'op_return_post_op_return_bytes')
|
||||
self.carrier_tx_count: AverageBlockCumulativeSumPattern[StoredU64] = AverageBlockCumulativeSumPattern(client, 'op_return_carrier_tx_count')
|
||||
self.carrier_vsize: AverageBlockCumulativeSumPattern[VSize] = AverageBlockCumulativeSumPattern(client, 'op_return_carrier_vsize')
|
||||
self.data_share: BpsPercentRatioPattern2 = BpsPercentRatioPattern2(client, 'op_return_data_share')
|
||||
|
||||
class SeriesTree_OpReturn_ByKind:
|
||||
"""Series tree node."""
|
||||
@@ -5097,10 +5109,10 @@ class SeriesTree_OpReturn_Policy:
|
||||
"""Series tree node."""
|
||||
|
||||
def __init__(self, client: BrkClient, base_path: str = ''):
|
||||
self.standard: CarrierOutputPostPattern = CarrierOutputPostPattern(client, 'op_return_policy_standard')
|
||||
self.oversized: CarrierOutputPostPattern = CarrierOutputPostPattern(client, 'op_return_policy_oversized')
|
||||
self.multiple: CarrierOutputPostPattern = CarrierOutputPostPattern(client, 'op_return_policy_multiple')
|
||||
self.pre_v30_nonstandard: CarrierOutputPostPattern = CarrierOutputPostPattern(client, 'op_return_policy_pre_v30_nonstandard')
|
||||
self.standard: CarrierDataOutputPostPattern = CarrierDataOutputPostPattern(client, 'op_return_policy_standard')
|
||||
self.oversized: CarrierDataOutputPostPattern = CarrierDataOutputPostPattern(client, 'op_return_policy_oversized')
|
||||
self.multiple: CarrierDataOutputPostPattern = CarrierDataOutputPostPattern(client, 'op_return_policy_multiple')
|
||||
self.pre_v30_nonstandard: CarrierDataOutputPostPattern = CarrierDataOutputPostPattern(client, 'op_return_policy_pre_v30_nonstandard')
|
||||
|
||||
class SeriesTree_OpReturn:
|
||||
"""Series tree node."""
|
||||
|
||||
@@ -164,7 +164,7 @@ export function createCohortFolderWithNupl(cohort) {
|
||||
createValuationSectionFull({ cohort, title }),
|
||||
createPricesSectionFull({ cohort, title }),
|
||||
createCostBasisSectionWithPercentiles({ cohort, title }),
|
||||
createProfitabilitySection({ cohort, title }),
|
||||
createProfitabilitySectionFull({ cohort, title }),
|
||||
createActivitySection({ cohort, title }),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -808,8 +808,8 @@ export function createProfitabilitySectionAll({ cohort, title }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Section for Full cohorts (STH)
|
||||
* @param {{ cohort: CohortFull, title: (name: string) => string }} args
|
||||
* Section for cohorts with full realized and unrealized profitability data.
|
||||
* @param {{ cohort: { tree: { unrealized: FullRelativePattern, realized: RealizedPattern } }, title: (name: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createProfitabilitySectionFull({ cohort, title }) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
exposedSubtree,
|
||||
reusedSubtree,
|
||||
} from "./shared.js";
|
||||
import { createOpReturnSection } from "./network/op-return.js";
|
||||
|
||||
/**
|
||||
* Create Network section
|
||||
@@ -829,8 +830,8 @@ export function createNetworkSection() {
|
||||
* @template {string} K
|
||||
* @param {Object} args
|
||||
* @param {string} args.label - Singular noun for count/tree labels ("Output" / "Prev-Out")
|
||||
* @param {Readonly<Record<K, CountPattern<number>>>} args.count
|
||||
* @param {Readonly<Record<K, CountPattern<number>>>} args.txCount
|
||||
* @param {Readonly<Record<K | "all", CountPattern<number>>>} args.count
|
||||
* @param {Readonly<Record<K | "all", CountPattern<number>>>} args.txCount
|
||||
* @param {Readonly<Record<K, PercentRatioCumulativePattern>>} args.share
|
||||
* @param {Readonly<Record<K, PercentRatioCumulativePattern>>} args.txShare
|
||||
* @param {ReadonlyArray<{key: K, name: string, color: Color, defaultActive: boolean}>} args.types
|
||||
@@ -845,6 +846,15 @@ export function createNetworkSection() {
|
||||
types,
|
||||
}) => {
|
||||
const lowerLabel = label.toLowerCase();
|
||||
const countTypes = /** @type {const} */ ([
|
||||
...types,
|
||||
{
|
||||
key: "all",
|
||||
name: "All",
|
||||
color: colors.default,
|
||||
defaultActive: false,
|
||||
},
|
||||
]);
|
||||
return [
|
||||
{
|
||||
name: "Compare",
|
||||
@@ -855,7 +865,7 @@ export function createNetworkSection() {
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${w.title} ${label} Count by Type`,
|
||||
bottom: types.map((t) =>
|
||||
bottom: countTypes.map((t) =>
|
||||
line({
|
||||
series: count[t.key].sum[w.key],
|
||||
name: t.name,
|
||||
@@ -868,7 +878,7 @@ export function createNetworkSection() {
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `Cumulative ${label} Count by Type`,
|
||||
bottom: types.map((t) =>
|
||||
bottom: countTypes.map((t) =>
|
||||
line({
|
||||
series: count[t.key].cumulative,
|
||||
name: t.name,
|
||||
@@ -898,7 +908,7 @@ export function createNetworkSection() {
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${w.title} Transactions by ${label} Type`,
|
||||
bottom: types.map((t) =>
|
||||
bottom: countTypes.map((t) =>
|
||||
line({
|
||||
series: txCount[t.key].sum[w.key],
|
||||
name: t.name,
|
||||
@@ -911,7 +921,7 @@ export function createNetworkSection() {
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `Cumulative Transactions by ${label} Type`,
|
||||
bottom: types.map((t) =>
|
||||
bottom: countTypes.map((t) =>
|
||||
line({
|
||||
series: txCount[t.key].cumulative,
|
||||
name: t.name,
|
||||
@@ -1231,6 +1241,8 @@ export function createNetworkSection() {
|
||||
],
|
||||
},
|
||||
|
||||
createOpReturnSection(),
|
||||
|
||||
// UTXOs
|
||||
{
|
||||
name: "UTXOs",
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { brk } from "../../utils/client.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { chartsFromCount, line } from "../series.js";
|
||||
import { groupedWindowsCumulative } from "../shared.js";
|
||||
|
||||
const TYPE_DEFINITIONS = /** @type {const} */ ([
|
||||
{ key: "runes", name: "Runes", defaultActive: true },
|
||||
{ key: "veriBlock", name: "VeriBlock", defaultActive: true },
|
||||
{ key: "omni", name: "Omni", defaultActive: true },
|
||||
{ key: "stacks", name: "Stacks", defaultActive: true },
|
||||
{ key: "blockstack", name: "Blockstack", defaultActive: false },
|
||||
{ key: "colu", name: "Colu", defaultActive: false },
|
||||
{ key: "openAssets", name: "Open Assets", defaultActive: false },
|
||||
{ key: "komodo", name: "Komodo", defaultActive: false },
|
||||
{ key: "coinSpark", name: "CoinSpark", defaultActive: false },
|
||||
{ key: "poet", name: "Po.et", defaultActive: false },
|
||||
{ key: "docproof", name: "Docproof", defaultActive: false },
|
||||
{ key: "openTimestamps", name: "OpenTimestamps", defaultActive: true },
|
||||
{ key: "factom", name: "Factom", defaultActive: false },
|
||||
{ key: "eternityWall", name: "Eternity Wall", defaultActive: false },
|
||||
{ key: "memo", name: "Memo", defaultActive: false },
|
||||
{ key: "bitproof", name: "Bitproof", defaultActive: false },
|
||||
{ key: "ascribe", name: "Ascribe", defaultActive: false },
|
||||
{ key: "stampery", name: "Stampery", defaultActive: false },
|
||||
{ key: "epobc", name: "EPOBC", defaultActive: false },
|
||||
{ key: "bareHash", name: "Bare Hash", defaultActive: false },
|
||||
{ key: "text", name: "Text", defaultActive: true },
|
||||
{ key: "empty", name: "Empty", defaultActive: false },
|
||||
{ key: "unknown", name: "Unknown", defaultActive: true },
|
||||
]);
|
||||
|
||||
const METRICS = /** @type {const} */ ([
|
||||
{
|
||||
key: "carrierTxCount",
|
||||
name: "Transactions",
|
||||
title: "Carrier Transaction Count",
|
||||
unit: Unit.count,
|
||||
},
|
||||
{
|
||||
key: "outputCount",
|
||||
name: "Outputs",
|
||||
title: "Output Count",
|
||||
unit: Unit.count,
|
||||
},
|
||||
{
|
||||
key: "carrierVsize",
|
||||
name: "vBytes",
|
||||
title: "Carrier Transaction vBytes",
|
||||
unit: Unit.vb,
|
||||
},
|
||||
{
|
||||
key: "postOpReturnBytes",
|
||||
name: "Data",
|
||||
title: "Data Bytes",
|
||||
unit: Unit.bytes,
|
||||
},
|
||||
]);
|
||||
|
||||
/** @typedef {(typeof brk.series.opReturn.byKind)[keyof typeof brk.series.opReturn.byKind]} OpReturnMetrics */
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {readonly { name: string, color: Color, defaultActive: boolean, pattern: OpReturnMetrics }[]} args.list
|
||||
* @param {string} args.category
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function createComparisons({ list, category }) {
|
||||
return METRICS.map((metric) => ({
|
||||
name: metric.name,
|
||||
tree: groupedWindowsCumulative({
|
||||
list,
|
||||
title: (title) => `OP_RETURN ${title}`,
|
||||
metricTitle: `${metric.title} by ${category}`,
|
||||
getWindowSeries: (entry, window) =>
|
||||
entry.pattern[metric.key].sum[window],
|
||||
getCumulativeSeries: (entry) =>
|
||||
entry.pattern[metric.key].cumulative,
|
||||
seriesFn: line,
|
||||
unit: metric.unit,
|
||||
}),
|
||||
}));
|
||||
}
|
||||
|
||||
/** @returns {PartialOptionsGroup} */
|
||||
export function createOpReturnSection() {
|
||||
const opReturn = brk.series.opReturn;
|
||||
const types = TYPE_DEFINITIONS.map((type, index) => ({
|
||||
...type,
|
||||
color: colors.at(index, TYPE_DEFINITIONS.length),
|
||||
pattern: opReturn.byKind[type.key],
|
||||
}));
|
||||
const policies = [
|
||||
{
|
||||
name: "Standard",
|
||||
color: colors.profit,
|
||||
defaultActive: true,
|
||||
pattern: opReturn.policy.standard,
|
||||
},
|
||||
{
|
||||
name: "Oversized",
|
||||
color: colors.loss,
|
||||
defaultActive: true,
|
||||
pattern: opReturn.policy.oversized,
|
||||
},
|
||||
{
|
||||
name: "Multiple Outputs",
|
||||
color: colors.bitcoin,
|
||||
defaultActive: true,
|
||||
pattern: opReturn.policy.multiple,
|
||||
},
|
||||
{
|
||||
name: "Pre-v30 Nonstandard",
|
||||
color: colors.gray,
|
||||
defaultActive: true,
|
||||
pattern: opReturn.policy.preV30Nonstandard,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
name: "OP_RETURN",
|
||||
tree: [
|
||||
{
|
||||
name: "Total",
|
||||
tree: [
|
||||
{
|
||||
name: "Transactions",
|
||||
tree: chartsFromCount({
|
||||
pattern: opReturn.total.carrierTxCount,
|
||||
metric: "OP_RETURN Carrier Transaction Count",
|
||||
unit: Unit.count,
|
||||
color: colors.scriptType.opReturn,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "vBytes",
|
||||
tree: chartsFromCount({
|
||||
pattern: opReturn.total.carrierVsize,
|
||||
metric: "OP_RETURN Carrier Transaction vBytes",
|
||||
unit: Unit.vb,
|
||||
color: colors.scriptType.opReturn,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Data",
|
||||
tree: chartsFromCount({
|
||||
pattern: opReturn.total.postOpReturnBytes,
|
||||
metric: "OP_RETURN Data Bytes",
|
||||
unit: Unit.bytes,
|
||||
color: colors.scriptType.opReturn,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
title: "Cumulative OP_RETURN Data Share of Blockchain Size",
|
||||
bottom: [
|
||||
line({
|
||||
series: opReturn.total.dataShare.percent,
|
||||
name: "OP_RETURN Data",
|
||||
color: colors.scriptType.opReturn,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Type",
|
||||
tree: createComparisons({ list: types, category: "Type" }),
|
||||
},
|
||||
{
|
||||
name: "Policy",
|
||||
tree: [
|
||||
{
|
||||
name: "Share",
|
||||
title: "Cumulative OP_RETURN Data Share of Blockchain Size by Policy",
|
||||
bottom: policies.map(({ name, color, defaultActive, pattern }) =>
|
||||
line({
|
||||
series: pattern.dataShare.percent,
|
||||
name,
|
||||
color,
|
||||
defaultActive,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...createComparisons({ list: policies, category: "Policy" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,15 @@ export const HALVING_EPOCH_BLOCKS = 210_000;
|
||||
|
||||
export const MAX_BLOCK_WEIGHT = 4_000_000;
|
||||
|
||||
const RELATIVE_TIME = new Intl.RelativeTimeFormat(undefined);
|
||||
const RELATIVE_UNITS = /** @type {const} */ ([
|
||||
["year", 31_557_600],
|
||||
["month", 2_629_800],
|
||||
["day", 86_400],
|
||||
["hour", 3_600],
|
||||
["minute", 60],
|
||||
]);
|
||||
|
||||
/** @param {number} value */
|
||||
export function formatNumber(value) {
|
||||
return value.toLocaleString();
|
||||
@@ -22,6 +31,23 @@ export function formatDateTime(unixSeconds) {
|
||||
});
|
||||
}
|
||||
|
||||
/** @param {number} unixSeconds */
|
||||
export function formatDateAndAge(unixSeconds) {
|
||||
const date = new Date(unixSeconds * 1_000).toLocaleDateString(undefined, {
|
||||
dateStyle: "medium",
|
||||
});
|
||||
const difference = unixSeconds - Date.now() / 1_000;
|
||||
const absolute = Math.abs(difference);
|
||||
|
||||
if (absolute < 60) return `${date} · just now`;
|
||||
|
||||
const [unit, seconds] = RELATIVE_UNITS.find(([, duration]) => {
|
||||
return absolute >= duration;
|
||||
}) ?? RELATIVE_UNITS.at(-1);
|
||||
|
||||
return `${date} · ${RELATIVE_TIME.format(Math.trunc(difference / seconds), unit)}`;
|
||||
}
|
||||
|
||||
/** @param {number} bytes */
|
||||
export function formatBytes(bytes) {
|
||||
return bytes >= 1_000_000
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createUsdAmount, renderUsdAmount } from "../../../usd/index.js";
|
||||
import { formatDateTime } from "../format.js";
|
||||
import { formatDateAndAge } from "../format.js";
|
||||
import { createBlockTitle } from "../title/index.js";
|
||||
|
||||
/** @typedef {import("../../../modules/brk-client/index.js").BlockInfoV1} Block */
|
||||
@@ -24,38 +24,37 @@ function createHashElement(hash) {
|
||||
/** @param {Node[]} [actions] */
|
||||
export function createBlockHeader(actions = []) {
|
||||
const element = document.createElement("header");
|
||||
const titleRow = document.createElement("div");
|
||||
const main = document.createElement("div");
|
||||
const title = document.createElement("h1");
|
||||
const side = document.createElement("div");
|
||||
const actionList = document.createElement("div");
|
||||
const date = document.createElement("time");
|
||||
const hash = document.createElement("p");
|
||||
const price = createUsdAmount("output", 0, {
|
||||
size: "title",
|
||||
tone: "positive",
|
||||
});
|
||||
let timestamp = 0;
|
||||
let dateTimer = 0;
|
||||
|
||||
element.dataset.blockHeader = "";
|
||||
title.dataset.blockTitleText = "";
|
||||
main.dataset.blockMain = "";
|
||||
titleRow.dataset.blockTitle = "";
|
||||
side.dataset.blockSide = "";
|
||||
actionList.dataset.blockActions = "";
|
||||
date.dataset.blockDate = "";
|
||||
hash.dataset.blockHashLine = "";
|
||||
actionList.append(...actions);
|
||||
side.append(date, price, actionList);
|
||||
main.append(title, hash);
|
||||
titleRow.append(main, side);
|
||||
element.append(titleRow);
|
||||
element.append(title, price, hash, actionList, date);
|
||||
|
||||
function refreshDate() {
|
||||
date.textContent = formatDateAndAge(timestamp);
|
||||
dateTimer = window.setTimeout(refreshDate, 60_000);
|
||||
}
|
||||
|
||||
/** @param {Block} block */
|
||||
function update(block) {
|
||||
title.replaceChildren(...createBlockTitle(block.height));
|
||||
date.dateTime = new Date(block.timestamp * 1_000).toISOString();
|
||||
date.textContent = formatDateTime(block.timestamp);
|
||||
timestamp = block.timestamp;
|
||||
window.clearTimeout(dateTimer);
|
||||
refreshDate();
|
||||
hash.replaceChildren(createHashElement(block.id));
|
||||
renderUsdAmount(price, block.extras.price, {
|
||||
size: "title",
|
||||
tone: "positive",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,44 +5,41 @@
|
||||
--block-title-label-size: 2.5rem;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 0.15rem 1rem;
|
||||
align-items: baseline;
|
||||
padding-bottom: 1.25rem;
|
||||
|
||||
[data-dim] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
[data-block-title] {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 0.35rem 1rem;
|
||||
align-items: start;
|
||||
> h1 {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
[data-block-main] {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
min-width: 0;
|
||||
> output {
|
||||
justify-self: end;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-block-date],
|
||||
[data-block-hash-line] {
|
||||
> p,
|
||||
> time {
|
||||
color: var(--gray);
|
||||
}
|
||||
|
||||
[data-block-date] {
|
||||
> p {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[data-block-side] {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
justify-items: end;
|
||||
min-width: max-content;
|
||||
> time {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
[data-block-actions] {
|
||||
> div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
@@ -60,11 +57,4 @@
|
||||
padding: 0.25rem 0.375rem;
|
||||
}
|
||||
}
|
||||
|
||||
[data-block-hash-line] {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +34,8 @@ export function orderTransactions(weights, feeRates) {
|
||||
* @param {number} rows
|
||||
*/
|
||||
export function packTransactions(cells, columns, rows) {
|
||||
let layouts = packCells(cells, columns, rows);
|
||||
|
||||
while (layouts === null) {
|
||||
rows += 1;
|
||||
layouts = packCells(cells, columns, rows);
|
||||
}
|
||||
|
||||
return { layouts, resolvedCells: cells };
|
||||
return {
|
||||
layouts: packCells(cells, columns, rows),
|
||||
resolvedCells: cells,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @param {readonly PackCell[]} cells
|
||||
* @param {number} columns
|
||||
* @param {number} rows
|
||||
* @returns {PackLayout[] | null}
|
||||
* @returns {PackLayout[]}
|
||||
*/
|
||||
export function packCells(cells, columns, rows) {
|
||||
const occupied = new Uint8Array(columns * rows);
|
||||
@@ -10,8 +10,8 @@ export function packCells(cells, columns, rows) {
|
||||
const layouts = [];
|
||||
|
||||
for (const cell of cells) {
|
||||
const span = Math.min(cell.span, columns);
|
||||
const position = findPosition(
|
||||
let span = Math.min(cell.span, columns);
|
||||
let position = findPosition(
|
||||
occupied,
|
||||
columns,
|
||||
rows,
|
||||
@@ -19,7 +19,16 @@ export function packCells(cells, columns, rows) {
|
||||
cursors[span],
|
||||
);
|
||||
|
||||
if (position === null) return null;
|
||||
while (position === null) {
|
||||
span -= 1;
|
||||
position = findPosition(
|
||||
occupied,
|
||||
columns,
|
||||
rows,
|
||||
span,
|
||||
cursors[span],
|
||||
);
|
||||
}
|
||||
|
||||
fillCells(occupied, columns, position.x, position.y, span);
|
||||
cursors[span] = position.index + span;
|
||||
|
||||
@@ -76,6 +76,7 @@ function createReceiptBrand() {
|
||||
/** @param {Block} block */
|
||||
function openReceiptDialog(block) {
|
||||
const dialog = document.createElement("dialog");
|
||||
const content = document.createElement("main");
|
||||
const paper = document.createElement("article");
|
||||
const controls = document.createElement("footer");
|
||||
const print = document.createElement("button");
|
||||
@@ -104,14 +105,15 @@ function openReceiptDialog(block) {
|
||||
createReceiptQr(block, url),
|
||||
createReceiptBrand(),
|
||||
);
|
||||
dialog.append(paper, controls);
|
||||
content.append(paper);
|
||||
dialog.append(content, controls);
|
||||
|
||||
print.addEventListener("click", () => {
|
||||
window.print();
|
||||
});
|
||||
openDialog(dialog, document.body);
|
||||
dialog.focus({ preventScroll: true });
|
||||
dialog.scrollTop = 0;
|
||||
content.scrollTop = 0;
|
||||
}
|
||||
|
||||
export function createBlockReceipt() {
|
||||
|
||||
@@ -10,18 +10,25 @@ dialog[data-block-receipt] {
|
||||
position: fixed;
|
||||
inset: 0 0 auto;
|
||||
width: min(100% - 2rem, 28rem);
|
||||
height: 100dvh;
|
||||
max-height: 100dvh;
|
||||
margin: 0 auto;
|
||||
border-radius: 0;
|
||||
overflow-y: auto;
|
||||
padding-block: var(--receipt-dialog-padding-block);
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
color: var(--black);
|
||||
background: transparent;
|
||||
font-family: var(--font-mono);
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: none;
|
||||
translate: none;
|
||||
|
||||
> main {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding-block: var(--receipt-dialog-padding-block);
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
:is(
|
||||
[data-receipt-section] > h3,
|
||||
[data-receipt-row] > span,
|
||||
@@ -190,7 +197,7 @@ dialog[data-block-receipt] {
|
||||
}
|
||||
|
||||
[data-receipt-controls] {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
bottom: 0.5rem;
|
||||
left: 50%;
|
||||
z-index: 1;
|
||||
@@ -265,6 +272,7 @@ dialog[data-block-receipt] {
|
||||
position: static;
|
||||
display: block;
|
||||
width: var(--receipt-page-width);
|
||||
height: auto;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
margin: 0;
|
||||
@@ -275,6 +283,12 @@ dialog[data-block-receipt] {
|
||||
overscroll-behavior: auto;
|
||||
}
|
||||
|
||||
dialog[data-block-receipt] > main {
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dialog[data-block-receipt]::backdrop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user