refactor(v2): rename net_sats → principal_sats for semantic clarity
`net` is financial-accounting ambiguous (net of what?). In the
bitSpire/DCA context this column is specifically the principal the
operator distributes to LPs (gross − commission), not a generic net
amount. Renaming locally before any bitSpire firmware locks the
wire-level name; lamassu-next#44 should adopt the same name.
Scope:
- migrations.py: m003 ALTER TABLE … RENAME COLUMN, idempotent probe
pattern matching m002. Also updates the m001 canonical schema so
fresh installs land on the new column directly.
- models.py: `CreateDcaSettlementData.principal_sats` /
`DcaSettlement.principal_sats`. Field-doc comment updated.
- bitspire.py: both happy path and fallback path return
`principal_sats=…`. Reads `extra.get("principal_sats")` from the
bitSpire payload (lamassu-next#44 should follow this rename).
- crud.py: INSERT column list + `apply_partial_dispense(
new_principal_sats=…)` keyword.
- distribution.py: every `settlement.net_sats` → `settlement.
principal_sats`; partial-dispense memo + helper signatures updated;
the leg-order docblock at the top reads "principal_sats".
- tasks.py: landed-settlement log line.
- static/js/index.js: settlements-table column `principal_sats` with
label "Principal (→ LPs)".
- templates/satmachineadmin/index.html: q-td key + binding.
All 86 unit tests still pass. No backwards-compat shim — v2-bitspire
isn't released; the rename is a clean break.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9414a18f82
commit
1feaba80ed
8 changed files with 97 additions and 106 deletions
|
|
@ -59,16 +59,14 @@ async def m001_satmachine_v2_initial(db):
|
|||
await db.execute(f"DROP TABLE IF EXISTS satoshimachine.{table}")
|
||||
|
||||
# 2. super_config — singleton (id='default') with platform-fee config.
|
||||
await db.execute(
|
||||
f"""
|
||||
await db.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.super_config (
|
||||
id TEXT PRIMARY KEY,
|
||||
super_fee_pct DECIMAL(10,4) NOT NULL DEFAULT 0.0000,
|
||||
super_fee_wallet_id TEXT,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
existing = await db.fetchone(
|
||||
"SELECT id FROM satoshimachine.super_config WHERE id = 'default'"
|
||||
)
|
||||
|
|
@ -80,8 +78,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
|
||||
# 3. dca_machines — one row per bitSpire ATM, owned by exactly one
|
||||
# operator. wallet_id UNIQUE prevents the IDOR funds-theft vector.
|
||||
await db.execute(
|
||||
f"""
|
||||
await db.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.dca_machines (
|
||||
id TEXT PRIMARY KEY,
|
||||
operator_user_id TEXT NOT NULL,
|
||||
|
|
@ -95,8 +92,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
await db.execute(
|
||||
"CREATE INDEX IF NOT EXISTS dca_machines_operator_idx "
|
||||
"ON dca_machines (operator_user_id)"
|
||||
|
|
@ -109,8 +105,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
# 4. dca_clients — LP registrations scoped per (machine, user). An LP
|
||||
# can hold positions at many machines (and many operators) on the
|
||||
# same LNbits instance.
|
||||
await db.execute(
|
||||
f"""
|
||||
await db.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.dca_clients (
|
||||
id TEXT PRIMARY KEY,
|
||||
machine_id TEXT NOT NULL,
|
||||
|
|
@ -125,21 +120,18 @@ async def m001_satmachine_v2_initial(db):
|
|||
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
await db.execute(
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS dca_clients_machine_user_uq "
|
||||
"ON dca_clients (machine_id, user_id)"
|
||||
)
|
||||
await db.execute(
|
||||
"CREATE INDEX IF NOT EXISTS dca_clients_user_idx "
|
||||
"ON dca_clients (user_id)"
|
||||
"CREATE INDEX IF NOT EXISTS dca_clients_user_idx " "ON dca_clients (user_id)"
|
||||
)
|
||||
|
||||
# 5. dca_deposits — fiat the operator (or super) records against an LP
|
||||
# at a machine. creator_user_id preserves audit trail.
|
||||
await db.execute(
|
||||
f"""
|
||||
await db.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.dca_deposits (
|
||||
id TEXT PRIMARY KEY,
|
||||
client_id TEXT NOT NULL,
|
||||
|
|
@ -152,8 +144,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now},
|
||||
confirmed_at TIMESTAMP
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
await db.execute(
|
||||
"CREATE INDEX IF NOT EXISTS dca_deposits_client_idx "
|
||||
"ON dca_deposits (client_id, created_at DESC)"
|
||||
|
|
@ -170,8 +161,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
# ships, these two columns are the audit-grade record of who
|
||||
# forgave what per transaction. Do not collapse them into a single
|
||||
# commission_pct. See plan section "Customer discounts" and #10.
|
||||
await db.execute(
|
||||
f"""
|
||||
await db.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.dca_settlements (
|
||||
id TEXT PRIMARY KEY,
|
||||
machine_id TEXT NOT NULL,
|
||||
|
|
@ -182,7 +172,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
fiat_amount DECIMAL(10,2) NOT NULL,
|
||||
fiat_code TEXT NOT NULL DEFAULT 'GTQ',
|
||||
exchange_rate REAL NOT NULL,
|
||||
net_sats BIGINT NOT NULL,
|
||||
principal_sats BIGINT NOT NULL,
|
||||
commission_sats BIGINT NOT NULL,
|
||||
platform_fee_sats BIGINT NOT NULL,
|
||||
operator_fee_sats BIGINT NOT NULL,
|
||||
|
|
@ -197,8 +187,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
notes TEXT,
|
||||
processing_claim TEXT
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
await db.execute(
|
||||
"CREATE INDEX IF NOT EXISTS dca_settlements_machine_idx "
|
||||
"ON dca_settlements (machine_id, created_at DESC)"
|
||||
|
|
@ -216,8 +205,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
# - Lightning address (user@domain)
|
||||
# - LNURL string (bech32 LNURL...)
|
||||
# Resolution lives in distribution._pay_one_split_leg.
|
||||
await db.execute(
|
||||
f"""
|
||||
await db.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.dca_commission_splits (
|
||||
id TEXT PRIMARY KEY,
|
||||
machine_id TEXT,
|
||||
|
|
@ -228,8 +216,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
await db.execute(
|
||||
"CREATE INDEX IF NOT EXISTS dca_commission_splits_lookup_idx "
|
||||
"ON dca_commission_splits (operator_user_id, machine_id)"
|
||||
|
|
@ -239,8 +226,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
# discriminator: dca | super_fee | operator_split | settlement |
|
||||
# autoforward | refund. status enum: pending | completed | failed |
|
||||
# voided | skipped | refunded.
|
||||
await db.execute(
|
||||
f"""
|
||||
await db.execute(f"""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.dca_payments (
|
||||
id TEXT PRIMARY KEY,
|
||||
settlement_id TEXT,
|
||||
|
|
@ -259,8 +245,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
error_message TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
await db.execute(
|
||||
"CREATE INDEX IF NOT EXISTS dca_payments_client_idx "
|
||||
"ON dca_payments (client_id, created_at DESC)"
|
||||
|
|
@ -280,8 +265,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
# only cash_in/cash_out/cash_level/fiat/model — post-#43 fields
|
||||
# (name, location, geo, fees, limits, denominations, version) are
|
||||
# nullable until that upstream issue lands. Ingest opportunistically.
|
||||
await db.execute(
|
||||
"""
|
||||
await db.execute("""
|
||||
CREATE TABLE IF NOT EXISTS satoshimachine.dca_telemetry (
|
||||
machine_id TEXT PRIMARY KEY,
|
||||
beacon_cash_in BOOLEAN,
|
||||
|
|
@ -300,8 +284,7 @@ async def m001_satmachine_v2_initial(db):
|
|||
telemetry_json TEXT,
|
||||
telemetry_received_at TIMESTAMP
|
||||
);
|
||||
"""
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
async def m002_rename_commission_split_wallet_id_to_target(db):
|
||||
|
|
@ -332,3 +315,24 @@ async def m002_rename_commission_split_wallet_id_to_target(db):
|
|||
"ALTER TABLE satoshimachine.dca_commission_splits "
|
||||
"RENAME COLUMN wallet_id TO target"
|
||||
)
|
||||
|
||||
|
||||
async def m003_rename_settlements_net_sats_to_principal_sats(db):
|
||||
"""Rename `dca_settlements.net_sats` → `principal_sats` for clarity.
|
||||
|
||||
"Net" in financial accounting is overloaded (net of what?). In the
|
||||
bitSpire/DCA context this column is specifically the principal the
|
||||
operator distributes to LPs (gross − commission), not a generic
|
||||
"net" amount. Renaming locally before any bitSpire firmware locks
|
||||
the wire-level name; lamassu-next#44 should adopt the same name.
|
||||
|
||||
Idempotent: probes for the old `net_sats` column. If present, rename.
|
||||
"""
|
||||
try:
|
||||
await db.fetchone("SELECT net_sats FROM satoshimachine.dca_settlements LIMIT 1")
|
||||
except Exception:
|
||||
return
|
||||
await db.execute(
|
||||
"ALTER TABLE satoshimachine.dca_settlements "
|
||||
"RENAME COLUMN net_sats TO principal_sats"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue