mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-04 19:29:09 -07:00
global: snapshot
This commit is contained in:
@@ -6,7 +6,7 @@ use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedFromHeightFull, WindowStarts},
|
||||
internal::{ComputedFromHeightSum, WindowStarts},
|
||||
};
|
||||
|
||||
use super::TotalAddrCountVecs;
|
||||
@@ -14,9 +14,9 @@ use super::TotalAddrCountVecs;
|
||||
/// New address count per block (global + per-type)
|
||||
#[derive(Traversable)]
|
||||
pub struct NewAddrCountVecs<M: StorageMode = Rw> {
|
||||
pub all: ComputedFromHeightFull<StoredU64, M>,
|
||||
pub all: ComputedFromHeightSum<StoredU64, M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: ByAddressType<ComputedFromHeightFull<StoredU64, M>>,
|
||||
pub by_addresstype: ByAddressType<ComputedFromHeightSum<StoredU64, M>>,
|
||||
}
|
||||
|
||||
impl NewAddrCountVecs {
|
||||
@@ -25,11 +25,11 @@ impl NewAddrCountVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all = ComputedFromHeightFull::forced_import(db, "new_addr_count", version, indexes)?;
|
||||
let all = ComputedFromHeightSum::forced_import(db, "new_addr_count", version, indexes)?;
|
||||
|
||||
let by_addresstype: ByAddressType<ComputedFromHeightFull<StoredU64>> =
|
||||
let by_addresstype: ByAddressType<ComputedFromHeightSum<StoredU64>> =
|
||||
ByAddressType::new_with_name(|name| {
|
||||
ComputedFromHeightFull::forced_import(
|
||||
ComputedFromHeightSum::forced_import(
|
||||
db,
|
||||
&format!("{name}_new_addr_count"),
|
||||
version,
|
||||
|
||||
@@ -96,7 +96,7 @@ impl AddressCohorts {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.par_iter_mut().try_for_each(|v| {
|
||||
v.addr_count_change_1m.height.compute_rolling_change(
|
||||
v.addr_count_delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&v.addr_count.height,
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::path::Path;
|
||||
use brk_cohort::{CohortContext, Filter, Filtered};
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, Height, Indexes, StoredF64, StoredU64, Version};
|
||||
use brk_types::{Cents, Height, Indexes, StoredI64, StoredU64, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
blocks,
|
||||
distribution::state::{AddressCohortState, MinimalRealizedState},
|
||||
indexes,
|
||||
internal::ComputedFromHeight,
|
||||
internal::{ComputedFromHeight, RollingDelta1m},
|
||||
prices,
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ pub struct AddressCohortVecs<M: StorageMode = Rw> {
|
||||
pub metrics: MinimalCohortMetrics<M>,
|
||||
|
||||
pub addr_count: ComputedFromHeight<StoredU64, M>,
|
||||
pub addr_count_change_1m: ComputedFromHeight<StoredF64, M>,
|
||||
pub addr_count_delta: RollingDelta1m<StoredU64, StoredI64, M>,
|
||||
}
|
||||
|
||||
impl AddressCohortVecs {
|
||||
@@ -64,10 +64,10 @@ impl AddressCohortVecs {
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
addr_count_change_1m: ComputedFromHeight::forced_import(
|
||||
addr_count_delta: RollingDelta1m::forced_import(
|
||||
db,
|
||||
&cfg.name("addr_count_change_1m"),
|
||||
version,
|
||||
&cfg.name("addr_count_delta"),
|
||||
version + Version::ONE,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Cents, Dollars, Height, Indexes, StoredF32, Version};
|
||||
use brk_types::{
|
||||
Bitcoin, Cents, Dollars, Height, Indexes, Sats, SatsSigned, StoredF32, StoredI64, StoredU64,
|
||||
Version,
|
||||
};
|
||||
use vecdb::AnyStoredVec;
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::internal::ComputedFromHeight;
|
||||
use crate::internal::{ComputedFromHeight, RollingDeltaExcept1m};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityFull, CohortMetricsBase, CostBasisWithExtended, ImportConfig, OutputsMetrics,
|
||||
@@ -31,6 +34,9 @@ pub struct AllCohortMetrics<M: StorageMode = Rw> {
|
||||
pub relative: Box<RelativeForAll<M>>,
|
||||
pub dormancy: ComputedFromHeight<StoredF32, M>,
|
||||
pub velocity: ComputedFromHeight<StoredF32, M>,
|
||||
|
||||
pub supply_delta_extended: RollingDeltaExcept1m<Sats, SatsSigned, M>,
|
||||
pub utxo_count_delta_extended: RollingDeltaExcept1m<StoredU64, StoredI64, M>,
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for AllCohortMetrics {
|
||||
@@ -82,6 +88,8 @@ impl AllCohortMetrics {
|
||||
relative: Box::new(relative),
|
||||
dormancy: cfg.import("dormancy", Version::ONE)?,
|
||||
velocity: cfg.import("velocity", Version::ONE)?,
|
||||
supply_delta_extended: cfg.import("supply_delta", Version::ONE)?,
|
||||
utxo_count_delta_extended: cfg.import("utxo_count_delta", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -123,6 +131,20 @@ impl AllCohortMetrics {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let window_starts = blocks.count.window_starts();
|
||||
self.supply_delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.supply.total.sats.height,
|
||||
exit,
|
||||
)?;
|
||||
self.utxo_count_delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.outputs.utxo_count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dormancy.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.activity.coindays_destroyed.height,
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Dollars, Height, Indexes, Sats, StoredF32, Version};
|
||||
use brk_types::{
|
||||
Bitcoin, Dollars, Height, Indexes, Sats, SatsSigned, StoredF32, StoredI64, StoredU64, Version,
|
||||
};
|
||||
use vecdb::AnyStoredVec;
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{blocks, prices};
|
||||
|
||||
use crate::internal::ComputedFromHeight;
|
||||
use crate::internal::{ComputedFromHeight, RollingDeltaExcept1m};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityFull, CohortMetricsBase, CostBasisWithExtended, ImportConfig, OutputsMetrics,
|
||||
@@ -29,6 +31,9 @@ pub struct ExtendedCohortMetrics<M: StorageMode = Rw> {
|
||||
pub relative: Box<RelativeWithExtended<M>>,
|
||||
pub dormancy: ComputedFromHeight<StoredF32, M>,
|
||||
pub velocity: ComputedFromHeight<StoredF32, M>,
|
||||
|
||||
pub supply_delta_extended: RollingDeltaExcept1m<Sats, SatsSigned, M>,
|
||||
pub utxo_count_delta_extended: RollingDeltaExcept1m<StoredU64, StoredI64, M>,
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for ExtendedCohortMetrics {
|
||||
@@ -72,6 +77,8 @@ impl ExtendedCohortMetrics {
|
||||
relative: Box::new(relative),
|
||||
dormancy: cfg.import("dormancy", Version::ONE)?,
|
||||
velocity: cfg.import("velocity", Version::ONE)?,
|
||||
supply_delta_extended: cfg.import("supply_delta", Version::ONE)?,
|
||||
utxo_count_delta_extended: cfg.import("utxo_count_delta", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -103,6 +110,20 @@ impl ExtendedCohortMetrics {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
let window_starts = blocks.count.window_starts();
|
||||
self.supply_delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.supply.total.sats.height,
|
||||
exit,
|
||||
)?;
|
||||
self.utxo_count_delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.outputs.utxo_count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.dormancy.height.compute_transform2(
|
||||
starting_indexes.height,
|
||||
&self.activity.coindays_destroyed.height,
|
||||
|
||||
@@ -11,9 +11,9 @@ use crate::{
|
||||
internal::{
|
||||
CentsType, ComputedFromHeight, ComputedFromHeightCumulative,
|
||||
ComputedFromHeightCumulativeSum, ComputedFromHeightRatio, FiatFromHeight, NumericValue,
|
||||
PercentFromHeight, PercentRollingWindows, Price,
|
||||
PercentFromHeight, PercentRollingWindows, Price, RollingDelta1m, RollingDeltaExcept1m,
|
||||
RollingWindow24h, RollingWindows, RollingWindowsFrom1w,
|
||||
ValueFromHeight, ValueFromHeightChange, ValueFromHeightCumulative,
|
||||
ValueFromHeight, ValueFromHeightCumulative,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -39,7 +39,6 @@ macro_rules! impl_config_import {
|
||||
impl_config_import!(
|
||||
ValueFromHeight,
|
||||
ValueFromHeightCumulative,
|
||||
ValueFromHeightChange,
|
||||
ComputedFromHeightRatio,
|
||||
PercentFromHeight<BasisPoints16>,
|
||||
PercentFromHeight<BasisPoints32>,
|
||||
@@ -84,6 +83,19 @@ impl<C: CentsType> ConfigImport for FiatFromHeight<C> {
|
||||
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
|
||||
}
|
||||
}
|
||||
impl<S: NumericValue + JsonSchema, C: NumericValue + JsonSchema> ConfigImport for RollingDelta1m<S, C>
|
||||
{
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
|
||||
}
|
||||
}
|
||||
impl<S: NumericValue + JsonSchema, C: NumericValue + JsonSchema> ConfigImport
|
||||
for RollingDeltaExcept1m<S, C>
|
||||
{
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Self::forced_import(cfg.db, &cfg.name(suffix), cfg.version + offset, cfg.indexes)
|
||||
}
|
||||
}
|
||||
impl<T: BytesVecValue> ConfigImport for BytesVec<Height, T> {
|
||||
fn config_import(cfg: &ImportConfig, suffix: &str, offset: Version) -> Result<Self> {
|
||||
Ok(Self::forced_import(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, StoredF64, StoredU64, Version};
|
||||
use brk_types::{Height, Indexes, StoredI64, StoredU64, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{blocks, internal::ComputedFromHeight};
|
||||
use crate::{blocks, internal::{ComputedFromHeight, RollingDelta1m}};
|
||||
|
||||
use super::ImportConfig;
|
||||
|
||||
@@ -11,7 +11,7 @@ use super::ImportConfig;
|
||||
#[derive(Traversable)]
|
||||
pub struct OutputsMetrics<M: StorageMode = Rw> {
|
||||
pub utxo_count: ComputedFromHeight<StoredU64, M>,
|
||||
pub utxo_count_change_1m: ComputedFromHeight<StoredF64, M>,
|
||||
pub utxo_count_delta: RollingDelta1m<StoredU64, StoredI64, M>,
|
||||
}
|
||||
|
||||
impl OutputsMetrics {
|
||||
@@ -19,7 +19,7 @@ impl OutputsMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
utxo_count: cfg.import("utxo_count", Version::ZERO)?,
|
||||
utxo_count_change_1m: cfg.import("utxo_count_change_1m", Version::ZERO)?,
|
||||
utxo_count_delta: cfg.import("utxo_count_delta", Version::ONE)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ impl OutputsMetrics {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.utxo_count_change_1m.height.compute_rolling_change(
|
||||
self.utxo_count_delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.utxo_count.height,
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
distribution::state::RealizedOps,
|
||||
internal::{
|
||||
ComputedFromHeight, ComputedFromHeightCumulative, LazyFromHeight,
|
||||
NegCentsUnsignedToDollars, RatioCents64, RollingWindow24h,
|
||||
NegCentsUnsignedToDollars, RatioCents64, RollingDelta1m, RollingWindow24h,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
@@ -27,7 +27,7 @@ pub struct RealizedCore<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub minimal: RealizedMinimal<M>,
|
||||
|
||||
pub realized_cap_change_1m: ComputedFromHeight<CentsSigned, M>,
|
||||
pub realized_cap_delta: RollingDelta1m<Cents, CentsSigned, M>,
|
||||
|
||||
pub neg_realized_loss: LazyFromHeight<Dollars, Cents>,
|
||||
pub net_realized_pnl: ComputedFromHeightCumulative<CentsSigned, M>,
|
||||
@@ -63,7 +63,7 @@ impl RealizedCore {
|
||||
|
||||
Ok(Self {
|
||||
minimal,
|
||||
realized_cap_change_1m: cfg.import("realized_cap_change_1m", v0)?,
|
||||
realized_cap_delta: cfg.import("realized_cap_delta", v1)?,
|
||||
neg_realized_loss,
|
||||
net_realized_pnl,
|
||||
value_created,
|
||||
@@ -154,7 +154,7 @@ impl RealizedCore {
|
||||
self.minimal
|
||||
.compute_rest_part2(prices, starting_indexes, height_to_supply, exit)?;
|
||||
|
||||
self.realized_cap_change_1m.height.compute_rolling_change(
|
||||
self.realized_cap_delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.minimal.realized_cap_cents.height,
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::{
|
||||
ComputedFromHeightRatioStdDevBands, LazyFromHeight, PercentFromHeight,
|
||||
PercentRollingWindows, Price, RatioCents64, RatioCentsBp32,
|
||||
RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDollarsBp32,
|
||||
RollingWindows, RollingWindowsFrom1w,
|
||||
RollingDelta1m, RollingDeltaExcept1m, RollingWindows, RollingWindowsFrom1w,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
@@ -56,10 +56,13 @@ pub struct RealizedFull<M: StorageMode = Rw> {
|
||||
|
||||
pub gross_pnl_sum: RollingWindows<Cents, M>,
|
||||
|
||||
pub net_pnl_change_1m: ComputedFromHeight<CentsSigned, M>,
|
||||
pub net_pnl_delta: RollingDelta1m<CentsSigned, CentsSigned, M>,
|
||||
pub net_pnl_delta_extended: RollingDeltaExcept1m<CentsSigned, CentsSigned, M>,
|
||||
pub net_pnl_change_1m_rel_to_realized_cap: PercentFromHeight<BasisPointsSigned32, M>,
|
||||
pub net_pnl_change_1m_rel_to_market_cap: PercentFromHeight<BasisPointsSigned32, M>,
|
||||
|
||||
pub realized_cap_delta_extended: RollingDeltaExcept1m<Cents, CentsSigned, M>,
|
||||
|
||||
pub investor_price: Price<ComputedFromHeight<Cents, M>>,
|
||||
pub investor_price_ratio: ComputedFromHeightRatio<M>,
|
||||
|
||||
@@ -176,11 +179,13 @@ impl RealizedFull {
|
||||
capitulation_flow,
|
||||
profit_flow,
|
||||
gross_pnl_sum,
|
||||
net_pnl_change_1m: cfg.import("net_pnl_change_1m", Version::new(3))?,
|
||||
net_pnl_delta: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
net_pnl_delta_extended: cfg.import("net_pnl_delta", Version::new(5))?,
|
||||
net_pnl_change_1m_rel_to_realized_cap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_realized_cap", Version::new(4))?,
|
||||
net_pnl_change_1m_rel_to_market_cap: cfg
|
||||
.import("net_pnl_change_1m_rel_to_market_cap", Version::new(4))?,
|
||||
realized_cap_delta_extended: cfg.import("realized_cap_delta", Version::new(5))?,
|
||||
investor_price,
|
||||
investor_price_ratio,
|
||||
lower_price_band,
|
||||
@@ -425,28 +430,42 @@ impl RealizedFull {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Net PnL change 1m
|
||||
self.net_pnl_change_1m.height.compute_rolling_change(
|
||||
// Net PnL delta (1m base + 24h/1w/1y extended)
|
||||
self.net_pnl_delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.base.core.net_realized_pnl.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.net_realized_pnl.cumulative.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_change_1m_rel_to_realized_cap
|
||||
.compute_binary::<CentsSigned, Cents, RatioCentsSignedCentsBps32>(
|
||||
starting_indexes.height,
|
||||
&self.net_pnl_change_1m.height,
|
||||
&self.net_pnl_delta.change_1m.height,
|
||||
&self.base.core.minimal.realized_cap_cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.net_pnl_change_1m_rel_to_market_cap
|
||||
.compute_binary::<CentsSigned, Dollars, RatioCentsSignedDollarsBps32>(
|
||||
starting_indexes.height,
|
||||
&self.net_pnl_change_1m.height,
|
||||
&self.net_pnl_delta.change_1m.height,
|
||||
height_to_market_cap,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Realized cap delta extended (24h/1w/1y — 1m is in RealizedCore)
|
||||
self.realized_cap_delta_extended.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.base.core.minimal.realized_cap_cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Peak regret
|
||||
self.peak_regret_rel_to_realized_cap
|
||||
.compute_binary::<Cents, Cents, RatioCentsBp32>(
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, Sats, Version};
|
||||
use brk_types::{Height, Indexes, Sats, SatsSigned, Version};
|
||||
|
||||
use crate::{blocks, prices};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::internal::{
|
||||
HalveCents, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyValueFromHeight, ValueFromHeight,
|
||||
ValueFromHeightChange,
|
||||
HalveCents, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyValueFromHeight,
|
||||
RollingDelta1m, ValueFromHeight,
|
||||
};
|
||||
|
||||
use super::ImportConfig;
|
||||
@@ -17,8 +17,7 @@ use super::ImportConfig;
|
||||
pub struct SupplyMetrics<M: StorageMode = Rw> {
|
||||
pub total: ValueFromHeight<M>,
|
||||
pub halved: LazyValueFromHeight,
|
||||
/// 1-month change in supply (net position change) - sats, btc, usd
|
||||
pub change_1m: ValueFromHeightChange<M>,
|
||||
pub delta: RollingDelta1m<Sats, SatsSigned, M>,
|
||||
}
|
||||
|
||||
impl SupplyMetrics {
|
||||
@@ -33,12 +32,12 @@ impl SupplyMetrics {
|
||||
HalveDollars,
|
||||
>(&cfg.name("supply_halved"), &supply, cfg.version);
|
||||
|
||||
let change_1m = cfg.import("supply_change_1m", Version::ZERO)?;
|
||||
let delta = cfg.import("supply_delta", Version::ONE)?;
|
||||
|
||||
Ok(Self {
|
||||
total: supply,
|
||||
halved: supply_halved,
|
||||
change_1m,
|
||||
delta,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,11 +100,10 @@ impl SupplyMetrics {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.change_1m.compute_rolling(
|
||||
self.delta.compute(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.total.sats.height,
|
||||
&self.total.cents.height,
|
||||
exit,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user