Skip to content

Commit 634b5cb

Browse files
committed
feat: deployed to HyperEVM mainnet, and the config now describes what exists
XStockRegistry 0xA54BC1b31d17a6B9F76d6De1BE43B8efb8843c2B LaunchpadFactory 0xcEa3AcF9b70cE9807a99bcBdF0F93A437518Eaeb FeeVault 0xDB4cF53967e29AB3dc38cbDc47C6ceDB4d862020 HolderRewardVault 0xF7dce0CC413D6d7F855A742A02545253Bbb0cB92 ReferencePriceAdapter 0x772AEDd551E38de727248e2925F53aAc80BE2b32 WrappedXStockFactory 0xc7b674f6Ec9de46852A25897305292a3d1E18d63 13,014,016 gas, 0.0013 HYPE. Governance is a 2-of-3 Safe, treasury a separate Safe, launch fee 0.04 HYPE. Every address recorded here was read BACK from the chain rather than copied from the deploy log. A log records what a script believed it did. The governance quorum guard added earlier today did its job against the real Safe: getThreshold() = 2, getOwners() = 3, passed. Its refusals for 1-of-1 and 2-of-2 were not theoretical when it counted. Three config-sim checks had to be rewritten because they asserted the pre-deployment world - "no wrapper factory is deployed yet" was true this morning and false this afternoon. Rewritten rather than deleted: the factory is bound immutably into the registry, so a config naming a different one describes a registry that does not exist, and that is worth pinning. The guardian is the blocker that replaced it, and it is heavier than it looks. addAttestor and setQuorum are both onlyGovernance, so a compromised governance key can make itself the sole Stockback attestor and drain the reward vault after ACTIVATION_DELAY. The Guardian's cancel is the only brake, and setGuardian is onlyGovernance too - so an absent guardian is not a missing formality, it is an unopposed path to user money. assertProductionConfigReady refuses without one now. deployer is deliberately NOT recorded in PLATFORM_ACCOUNTS. It signed the deployment and holds no authority afterwards; listing it would suggest it is part of the running system.
1 parent 3b42ca9 commit 634b5cb

3 files changed

Lines changed: 171 additions & 18 deletions

File tree

docs/DECISION-LOG.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,3 +716,70 @@ protocol. Viable as a first market; not viable as the mechanism.
716716
**Still open, and it is not an engineering question:** whether to launch on Backed's `wSPYx`
717717
first — small, someone else's contract, proves the whole path with real money at low stakes —
718718
and open the rest once this wrapper is audited.
719+
720+
---
721+
722+
## D-018 — Deployed to HyperEVM mainnet
723+
724+
**Class:** RECORD (§178.4 release gate, partial)
725+
**Date:** Day 9
726+
727+
```text
728+
XStockRegistry 0xA54BC1b31d17a6B9F76d6De1BE43B8efb8843c2B
729+
LaunchpadFactory 0xcEa3AcF9b70cE9807a99bcBdF0F93A437518Eaeb
730+
FeeVault 0xDB4cF53967e29AB3dc38cbDc47C6ceDB4d862020
731+
HolderRewardVault 0xF7dce0CC413D6d7F855A742A02545253Bbb0cB92
732+
ReferencePriceAdapter 0x772AEDd551E38de727248e2925F53aAc80BE2b32
733+
WrappedXStockFactory 0xc7b674f6Ec9de46852A25897305292a3d1E18d63
734+
735+
governance 0x791fb66Ac5ff91eE7D3F1697f85c4D8b646D1e22 Safe 1.4.1, 2-of-3
736+
treasury 0xEd7709178EF1De028E965B4107f56b5AecBE92A2 Safe 1.4.1
737+
launch fee 40000000000000000 0.04 HYPE (~$3.38)
738+
739+
13,014,016 gas, 0.0013 HYPE
740+
```
741+
742+
**Every address above was read BACK from the chain, not copied from the deploy
743+
log.** A log records what a script believed it did; the two have no reason to
744+
agree if anything went wrong in between. `registry.WRAPPER_FACTORY()`,
745+
`factory.REGISTRY()`, both `governance()` calls, `treasury()` and `launchFee()`
746+
were all queried afterwards and matched.
747+
748+
**Three things the deployment guards proved, on the real chain rather than in a
749+
mock.** `_assertGovernanceQuorum` read `getThreshold()` = 2 and `getOwners()` = 3
750+
off the live Safe and let it through — the guard added the same day, against the
751+
arrangement it was written for. The refusals for a 1-of-1 and a 2-of-2 were
752+
therefore not theoretical when they mattered.
753+
754+
**What the deployment does NOT include, and why each is deliberate.**
755+
756+
`factory.router()` and `factory.referencePrice()` are both zero, and the
757+
allowlist is empty. A launch is refused outright. That is §279 working: the
758+
router's three HyperSwap addresses are immutable in its constructor and V-06 has
759+
no first-party confirmation, and the anchor feed is V-11 — Pyth's equity feeds on
760+
this chain are 63–561 days stale.
761+
762+
**What the block lanes cost, in order.** The deployment met V-20 three separate
763+
times, and only the first was expected:
764+
765+
1. `LaunchpadFactory` needs 7,360,896 gas, so the deployer needed the large-lane
766+
opt-in — a HyperCore action, which itself required the deployer to first
767+
become a Core user by receiving a Core asset. Neither is an EVM call.
768+
2. Foundry reads the block gas limit from the tip block, which is a small-lane
769+
block ~99% of the time, so the simulation died at exactly 3,000,000 before
770+
broadcasting anything. Hence `[profile.deploy]`.
771+
3. `launch()` itself was 3,068,481 — over the ceiling for **every creator**, not
772+
just for us. Fixed the same day by dropping `optimizer_runs` to 400, at a cost
773+
of 51 gas per trade.
774+
775+
The third was found only because the owner asked what a launch costs before
776+
setting the fee. It was 2% over: the margin that gets estimated rather than
777+
measured, and estimated it would have shipped.
778+
779+
**Still open, and the guardian is now the sharpest of them.** `addAttestor` and
780+
`setQuorum` are both `onlyGovernance`, so a compromised governance key can make
781+
itself the sole Stockback attestor and claim the reward vault after
782+
`ACTIVATION_DELAY`. The Guardian's cancel is the only brake — and `setGuardian`
783+
is `onlyGovernance` too, so it is not a brake at all until a Guardian Safe exists
784+
with signers governance does not control. `assertProductionConfigReady` now
785+
refuses without one.

packages/config/sim/env.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
REFERENCE_PRICE_FEEDS,
2525
GRADUATION_ROUTER,
2626
WRAPPER_FACTORY,
27+
PLATFORM_ACCOUNTS,
2728
} from "../src/chain.ts";
2829

2930
let failures = 0;
@@ -334,27 +335,47 @@ section("Production readiness is still gated (§279)");
334335
* governance COULD list. The refusal has to say that rather than leaving an
335336
* operator to conclude the allowlist is merely unfinished.
336337
*/
337-
check("no wrapper factory is deployed yet (D-017)", WRAPPER_FACTORY === null);
338-
339-
let namesWrapper = false;
340-
try {
341-
assertProductionConfigReady();
342-
} catch (error) {
343-
namesWrapper = error instanceof Error && error.message.includes("wrapper factory");
344-
}
338+
/*
339+
* The wrapper factory IS deployed now, and this check used to assert the
340+
* opposite — correctly, until Day 9.
341+
*
342+
* Rewritten rather than deleted, because "the factory exists" is worth
343+
* pinning: it is bound immutably into the registry, so a config that names a
344+
* different one is a config describing a registry that does not exist.
345+
*/
346+
check("the wrapper factory is deployed (D-017)", WRAPPER_FACTORY !== null);
347+
check(
348+
"and it is the one the registry is bound to",
349+
WRAPPER_FACTORY === "0xc7b674f6Ec9de46852A25897305292a3d1E18d63",
350+
);
345351

346-
check("and the refusal names the wrapper factory", namesWrapper);
352+
/*
353+
* The guardian is the one that replaced it as a blocker, and it is a heavier
354+
* one than it looks. `addAttestor` and `setQuorum` are both onlyGovernance,
355+
* so a compromised governance key can make itself the sole Stockback attestor
356+
* and drain the reward vault after ACTIVATION_DELAY. The guardian's cancel is
357+
* the only brake, and governance can also set the guardian — so an absent
358+
* guardian is not a missing formality, it is an unopposed path to user money.
359+
*/
360+
check("no guardian Safe yet (C-08, §588)", PLATFORM_ACCOUNTS.guardianSafe === null);
347361

348-
let saysWhy = false;
362+
let namesGuardian = false;
349363
try {
350364
assertProductionConfigReady();
351365
} catch (error) {
352-
saysWhy = error instanceof Error && error.message.includes("every xStock rebases");
366+
namesGuardian = error instanceof Error && error.message.includes("guardian Safe");
353367
}
354368

355-
// The reason, not only the fact. "Allowlist empty" invites someone to go fill
356-
// it; "every xStock rebases, so nothing can be listed" says why they cannot.
357-
check("and says why nothing can be listed without it", saysWhy);
369+
check("and the refusal names it", namesGuardian);
370+
371+
// Governance and treasury are live and verified on-chain. Asserted because
372+
// the whole point of the deployment guard was that these are not placeholders.
373+
check("governance is recorded", PLATFORM_ACCOUNTS.governanceSafe !== null);
374+
check("treasury is recorded", PLATFORM_ACCOUNTS.treasurySafe !== null);
375+
check(
376+
"and the deployer is deliberately NOT recorded",
377+
PLATFORM_ACCOUNTS.deployer === null,
378+
);
358379
}
359380

360381
console.log(failures === 0 ? "\nconfig: all checks passed" : `\nconfig: ${failures} FAILED`);

packages/config/src/chain.ts

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,36 @@ export const LIQUIDITY_LOCK: `0x${string}` | null = null;
109109
* mismatch between the two is a deployment error, and `assertProductionConfigReady`
110110
* cannot detect it — only reading `XStockRegistry.WRAPPER_FACTORY()` can.
111111
*/
112-
export const WRAPPER_FACTORY: `0x${string}` | null = null;
112+
export const WRAPPER_FACTORY: `0x${string}` | null =
113+
"0xc7b674f6Ec9de46852A25897305292a3d1E18d63";
114+
115+
/**
116+
* The deployed core, on HyperEVM mainnet.
117+
*
118+
* Deployed Day 9. Every address below was read BACK from the chain rather than
119+
* copied from the deployment log — a log records what a script believed it did,
120+
* and the two have no reason to agree if anything went wrong in between.
121+
*
122+
* Verified at deployment:
123+
*
124+
* registry.governance() the governance Safe
125+
* registry.WRAPPER_FACTORY() WRAPPER_FACTORY above, bound immutably
126+
* factory.REGISTRY() REGISTRY below
127+
* factory.governance() the governance Safe
128+
* factory.treasury() the treasury Safe
129+
* factory.launchFee() 40000000000000000 (0.04 HYPE)
130+
*
131+
* `router` and `referencePrice` on the factory are still zero, and the
132+
* allowlist is still empty. That is the state the deployment log describes as
133+
* NOT DONE, and a launch is refused until it changes — see
134+
* `assertProductionConfigReady`.
135+
*/
136+
export const XSTOCK_REGISTRY: `0x${string}` = "0xA54BC1b31d17a6B9F76d6De1BE43B8efb8843c2B";
137+
export const LAUNCHPAD_FACTORY: `0x${string}` = "0xcEa3AcF9b70cE9807a99bcBdF0F93A437518Eaeb";
138+
export const FEE_VAULT: `0x${string}` = "0xDB4cF53967e29AB3dc38cbDc47C6ceDB4d862020";
139+
export const HOLDER_REWARD_VAULT: `0x${string}` = "0xF7dce0CC413D6d7F855A742A02545253Bbb0cB92";
140+
export const REFERENCE_PRICE_ADAPTER: `0x${string}` =
141+
"0x772AEDd551E38de727248e2925F53aAc80BE2b32";
113142

114143
/**
115144
* Fee tiers enabled on the HyperSwap V3 factory.
@@ -151,10 +180,31 @@ export interface XStockEntry {
151180
readonly verified: boolean;
152181
}
153182

154-
/** Platform accounts (§555). All open pending the key ceremony (C-08, V-13). */
183+
/**
184+
* Platform accounts (§555).
185+
*
186+
* Governance and Treasury exist and are verified on-chain. The rest of the
187+
* six-account structure does not, and each absence blocks something specific
188+
* rather than being a formality:
189+
*
190+
* guardianSafe the ONLY brake on governance reaching the reward vault.
191+
* `addAttestor` and `setQuorum` are both `onlyGovernance`, so
192+
* without an independent guardian the 6-hour ACTIVATION_DELAY
193+
* has nobody to act inside it. §601 forbids it sharing
194+
* governance's signers, and that is the point of it.
195+
*
196+
* opsRelayer the graduation keeper. Needs the large block lane, like the
197+
* deployer did.
198+
*
199+
* founderProfit founder revenue. Blocks nothing technical.
200+
*
201+
* `deployer` is deliberately still null. It signed the deployment and holds no
202+
* authority afterwards — recording it here would suggest it is part of the
203+
* running system, and it is not.
204+
*/
155205
export const PLATFORM_ACCOUNTS = {
156-
governanceSafe: null,
157-
treasurySafe: null,
206+
governanceSafe: "0x791fb66Ac5ff91eE7D3F1697f85c4D8b646D1e22",
207+
treasurySafe: "0xEd7709178EF1De028E965B4107f56b5AecBE92A2",
158208
founderProfitSafe: null,
159209
guardianSafe: null,
160210
deployer: null,
@@ -232,6 +282,21 @@ export function assertProductionConfigReady(): void {
232282
"no wrapper factory deployed (D-017) — every xStock rebases, so nothing can be listed",
233283
);
234284
}
285+
286+
/*
287+
* The guardian, checked separately from the other platform accounts.
288+
*
289+
* It is the only thing standing between a compromised governance Safe and the
290+
* reward vault: `addAttestor` and `setQuorum` are both `onlyGovernance`, so
291+
* the holder of that key can make itself the sole attestor and claim the vault
292+
* after ACTIVATION_DELAY. The guardian's cancel is the brake, and it is not a
293+
* brake at all if governance can also set the guardian to itself.
294+
*/
295+
if (PLATFORM_ACCOUNTS.guardianSafe === null) {
296+
problems.push(
297+
"no guardian Safe (C-08, §588) — nothing can cancel a bad Stockback commitment",
298+
);
299+
}
235300
if (PLATFORM_ACCOUNTS.governanceSafe === null) problems.push("platform accounts unset (C-08)");
236301

237302
/*

0 commit comments

Comments
 (0)