💎 PAY_DIAMOND Deploy Tool — REAL PRODUCTION FACETS ⚠️ MAINNET
Tool v1.0.0
⚠️ REAL DEPLOY — real HolgerPayment production logic, real gas, real consequence.
This is NOT the toy tool. Every facet deployed here is real Payment logic
(views, admin approval gating, refunds, campaigns, plans, memberships, and the
money-moving PaymentCoreFacet). Go through the steps below IN ORDER, at your
own pace — every step is independent: you can stop after any step and resume
later, nothing is lost (progress auto-saves to this browser). If a contract is
already deployed and you know its address, paste it into that step's override
field instead of redeploying.
Progress backup Every resolved address auto-saves to this browser (localStorage key 'payDiamondDeployV1'). Export/Import move progress across devices/browsers; Reset clears everything (asks to confirm).
Step 1 — Connect wallet, verify OWNER + network
Strict OWNER gate, same as every other tool in this project — if the
connected wallet isn't exactly OWNER, every deploy/write button stays disabled.
Step 2 — Deploy DiamondCutFacet
Standard EIP-2535 diamondCut facet. No constructor args.
DiamondCutFacet:
Step 3 — Deploy DiamondLoupeFacet
Standard EIP-2535 loupe facet (facets()/facetAddress()/etc). No constructor args.
DiamondLoupeFacet:
Step 4 — Deploy OwnershipFacet
Standard EIP-2535 ownership facet (owner()/transferOwnership()). No constructor args.
Constructor: (OWNER, DiamondCutFacet address) — DiamondCutFacet's own selector is wired in automatically inside the constructor, per Diamond.sol. This is the address you'll call everything through from here on.
Diamond (the proxy address you'll call everything on):
Calls diamondCut on the Diamond from Step 5 to register Loupe's 4 selectors + Ownership's 2 selectors. Needs Steps 3, 4, 5 done first.
Step 7 — Deploy PaymentViewFacet
Pure reads — isActiveMember()/totalPaid()/etc. Zero state-mutation risk. Real production Payment logic, no constructor args.
PaymentViewFacet:
Step 8 — Deploy PaymentAdminFacet
Admin wallet/config setters, plus the 3 registration-approval functions folded in per T107: setPlanRequiresApproval/approveMembership/rejectMembership. No constructor args.
PaymentAdminFacet:
Step 9 — Deploy RefundFacet
Refund handling. No constructor args.
RefundFacet:
Step 10 — Deploy CampaignFacet
Campaign/slot/coupon logic (compiled with viaIR — Stack too deep otherwise). No constructor args.
CampaignFacet:
Step 11 — Deploy PlanFacet
Plan configuration. No constructor args.
PlanFacet:
Step 12 — Deploy MembershipFacet
Membership state transitions. No constructor args.
MembershipFacet:
Step 13 — Deploy PaymentCoreFacet
⚠️ THE MONEY-MOVING FACET — payForService and friends. Deploy LAST, only once every simpler facet on this Diamond is proven working, per the migration plan's explicit ordering. No constructor args.
Calls diamondCut once with all 7 Payment facets' real function selectors, derived
LIVE from each facet's real compiled ABI via ethers.Interface.getFunction(name).selector
— never hardcoded/guessed. Verified in advance (T110 compile/verification pass) that all 77
selectors across the 7 facets are distinct — no collisions. Needs Step 5 (Diamond) and all 7
facet addresses (Steps 7–13) done first.
Step 15 — Bootstrap Owner Wallet (REQUIRED — do this before any other admin-gated step)
⚠️ Critical one-time step. PaymentAdminFacet's business-logic
ownerWallet field (distinct from the Diamond's own EIP-173 technical
owner set in Step 5's constructor) starts at address(0) — every
onlyOwner-gated function reverts until this is set. This
bootstrapOwnerWallet(address) call is gated by the Diamond's REAL
technical owner (LibDiamond.contractOwner()), not by the broken
business-field check, and reverts if ownerWallet is already
non-zero — so it can only ever run once. Needs Step 5 (Diamond) + Step 8
(PaymentAdminFacet) + Step 14 (diamondCut wiring) done first.
Step 16 — Verify: read facets() through the Diamond
Calls facets() via DiamondLoupeFacet, routed through the Diamond's fallback — displays every registered facet address and how many selectors it owns, so the wiring can be visually confirmed.
Facet address
# selectors
Which contract (matched against deployed addresses above)
Step 17 — Optional: set auto-refund-on-reject (admin setter, T111)
Calls PaymentAdminFacet.setAutoRefundOnReject(bool) through the Diamond.
Default on-chain state after deploy is false — this step is entirely
optional and can be skipped; the Diamond behaves exactly like a plain deploy until
this is explicitly called. When enabled, every future rejectMembership
call auto-attempts a refund from the Diamond's own USDC balance regardless of the
per-call refund argument. Needs Step 5 (Diamond) + Step 8
(PaymentAdminFacet) + Step 14 (diamondCut wiring) done first. Safe to call again
later to flip the toggle either direction — not a one-time setup step.