Skip to content

Commit a2c657a

Browse files
authored
Merge pull request #9 from kojibai/main
v2.1.0
2 parents 92cfc93 + 6334edd commit a2c657a

File tree

10 files changed

+377
-133
lines changed

10 files changed

+377
-133
lines changed

package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"dependencies": {
1414
"@react-three/drei": "^9.100.0",
1515
"@react-three/fiber": "^8.15.19",
16+
"@stripe/react-stripe-js": "^5.4.1",
17+
"@stripe/stripe-js": "^8.6.0",
1618
"@tanstack/react-query": "^5.45.0",
1719
"clsx": "^2.1.1",
1820
"decimal.js": "^10.6.0",

src/SigilMarkets/SigilMarketsShell.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { SigilMarketsRoutes } from "./SigilMarketsRoutes";
1818

1919
import { usePulseTicker } from "./hooks/usePulseTicker";
2020
import { useSfx } from "./hooks/useSfx";
21+
import { useVisualViewportSize } from "../hooks/useVisualViewportSize";
2122

2223
import { fetchMarkets, type SigilMarketsMarketApiConfig } from "./api/marketApi";
2324
import type { SigilMarketsVaultApiConfig } from "./api/vaultApi";
@@ -312,9 +313,24 @@ const ShellInner = (props: Readonly<{ windowScroll: boolean }>) => {
312313

313314
export const SigilMarketsShell = (props: SigilMarketsShellProps) => {
314315
const windowScroll = props.windowScroll ?? false;
316+
const viewportSize = useVisualViewportSize();
317+
const rootStyle = useMemo(() => {
318+
const next = { ...(props.style ?? {}) } as React.CSSProperties & Record<string, string>;
319+
const safeHeight = viewportSize.height + viewportSize.offsetTop;
320+
if (safeHeight > 0) {
321+
next["--sm-vh"] = `${safeHeight}px`;
322+
}
323+
if (viewportSize.width > 0) {
324+
next["--sm-vw"] = `${viewportSize.width}px`;
325+
}
326+
if (viewportSize.offsetTop > 0) {
327+
next["--sm-vv-top"] = `${viewportSize.offsetTop}px`;
328+
}
329+
return next;
330+
}, [props.style, viewportSize.height, viewportSize.offsetTop, viewportSize.width]);
315331

316332
return (
317-
<div className={props.className} style={props.style} data-sm-root="1">
333+
<div className={props.className} style={rootStyle} data-sm-root="1">
318334
<SigilMarketsRuntimeConfigProvider
319335
marketApiConfig={props.marketApiConfig}
320336
vaultApiConfig={props.vaultApiConfig}

src/SigilMarkets/hooks/useProphecySigilVerification.ts

Lines changed: 80 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,49 @@ import {
1414
} from "../utils/prophecySigil";
1515
import type { ProphecySigilPayloadV1 } from "../types/prophecySigilTypes";
1616

17+
/* ──────────────────────────────────────────────────────────────────────────────
18+
ChakraDay coercion
19+
NOTE: KaiMoment (from marketTypes) does NOT include chakraDay — it's a minimal moment.
20+
So we define ChakraDay here (or import it from your canonical kai_pulse types).
21+
────────────────────────────────────────────────────────────────────────────── */
22+
type ChakraDay =
23+
| "Root"
24+
| "Sacral"
25+
| "Solar Plexus"
26+
| "Heart"
27+
| "Throat"
28+
| "Third Eye"
29+
| "Crown";
30+
31+
function asChakraDay(v: unknown): ChakraDay {
32+
const raw = String(v ?? "").trim();
33+
const s = raw.toLowerCase().replace(/[-_]/g, " ").replace(/\s+/g, " ");
34+
35+
switch (s) {
36+
case "root":
37+
return "Root";
38+
case "sacral":
39+
return "Sacral";
40+
case "solar plexus":
41+
case "solar":
42+
case "plexus":
43+
return "Solar Plexus";
44+
case "heart":
45+
return "Heart";
46+
case "throat":
47+
return "Throat";
48+
case "third eye":
49+
case "third":
50+
case "eye":
51+
return "Third Eye";
52+
case "crown":
53+
case "krown":
54+
return "Crown";
55+
default:
56+
return "Root";
57+
}
58+
}
59+
1760
const loadVkey = async (): Promise<unknown | null> => {
1861
try {
1962
const res = await fetch("/zk/verification_key.json", { cache: "no-store" });
@@ -34,7 +77,10 @@ export type ProphecySigilVerification = Readonly<{
3477
windowStatus: "open" | "closed" | "unknown";
3578
}>;
3679

37-
export const useProphecySigilVerification = (svg: string | undefined, now: KaiMoment): ProphecySigilVerification => {
80+
export const useProphecySigilVerification = (
81+
svg: string | undefined,
82+
now: KaiMoment,
83+
): ProphecySigilVerification => {
3884
const [state, setState] = useState<ProphecySigilVerification>({
3985
signatureOk: null,
4086
zkOk: null,
@@ -48,25 +94,30 @@ export const useProphecySigilVerification = (svg: string | undefined, now: KaiMo
4894
useEffect(() => {
4995
let cancelled = false;
5096

97+
const reset = (): void => {
98+
setState({
99+
signatureOk: null,
100+
zkOk: null,
101+
canonicalHashOk: null,
102+
text: "",
103+
windowStatus: "unknown",
104+
});
105+
};
106+
51107
const run = async (): Promise<void> => {
52108
if (!svgText) {
53-
if (!cancelled) {
54-
setState({
55-
signatureOk: null,
56-
zkOk: null,
57-
canonicalHashOk: null,
58-
text: "",
59-
windowStatus: "unknown",
60-
});
61-
}
109+
if (!cancelled) reset();
62110
return;
63111
}
64112

65113
const parsed = parseProphecySigilSvg(svgText);
66114
const payload = parsed.payload;
67-
const text = typeof payload.text === "string" ? payload.text : parsed.textDecoded ?? "";
68115

69-
const windowStatus =
116+
const textFromPayload = typeof payload.text === "string" ? payload.text : "";
117+
const textDecoded = parsed.textDecoded ?? "";
118+
const text = textFromPayload || textDecoded;
119+
120+
const windowStatus: ProphecySigilVerification["windowStatus"] =
70121
typeof payload.expirationPulse === "number"
71122
? now.pulse >= payload.expirationPulse
72123
? "closed"
@@ -79,7 +130,8 @@ export const useProphecySigilVerification = (svg: string | undefined, now: KaiMo
79130
: null;
80131

81132
let canonicalHashOk: boolean | null = null;
82-
let canonicalHash: string | undefined = typeof payload.canonicalHash === "string" ? payload.canonicalHash : undefined;
133+
let canonicalHash: string | undefined =
134+
typeof payload.canonicalHash === "string" ? payload.canonicalHash : undefined;
83135

84136
const hasCoreFields =
85137
typeof payload.prophecyId === "string" &&
@@ -94,6 +146,16 @@ export const useProphecySigilVerification = (svg: string | undefined, now: KaiMo
94146
typeof payload.chakraDay === "string";
95147

96148
if (hasCoreFields) {
149+
// Build moment as a variable (assignable even if buildProphecyPayloadBase uses a narrower moment type)
150+
const moment = {
151+
pulse: payload.pulse,
152+
beat: payload.beat,
153+
stepIndex: payload.stepIndex,
154+
stepPctAcrossBeat: payload.stepPct,
155+
chakraDay: asChakraDay(payload.chakraDay),
156+
weekday: "Solhara" as const,
157+
};
158+
97159
const base = buildProphecyPayloadBase({
98160
prophecyId: payload.prophecyId,
99161
text: payload.text,
@@ -104,18 +166,11 @@ export const useProphecySigilVerification = (svg: string | undefined, now: KaiMo
104166
evidence: payload.evidence,
105167
userPhiKey: payload.userPhiKey,
106168
kaiSignature: payload.kaiSignature,
107-
moment: {
108-
pulse: payload.pulse,
109-
beat: payload.beat,
110-
stepIndex: payload.stepIndex,
111-
stepPctAcrossBeat: payload.stepPct,
112-
chakraDay: payload.chakraDay,
113-
weekday: "Solhara",
114-
},
169+
moment,
115170
});
116171

117172
const computed = await computeProphecyCanonicalHash(base);
118-
canonicalHashOk = canonicalHash ? canonicalHash.toLowerCase() === computed : false;
173+
canonicalHashOk = canonicalHash ? canonicalHash.toLowerCase() === computed.toLowerCase() : false;
119174
canonicalHash = canonicalHash ?? computed;
120175
}
121176

@@ -125,8 +180,10 @@ export const useProphecySigilVerification = (svg: string | undefined, now: KaiMo
125180
const zk = parsed.zk ?? (payload.zk as ProphecySigilPayloadV1["zk"] | undefined);
126181
if (zk && zk.proof && zk.publicInputs && canonicalHash) {
127182
zkScheme = zk.scheme;
183+
128184
const poseidon = await computeZkPoseidonHash(canonicalHash);
129185
const publicInput0 = Array.isArray(zk.publicInputs) ? zk.publicInputs[0] : undefined;
186+
130187
const binds =
131188
(zk.poseidonHash ? zk.poseidonHash === poseidon.hash : true) &&
132189
(publicInput0 ? publicInput0 === poseidon.hash : true);
@@ -152,7 +209,7 @@ export const useProphecySigilVerification = (svg: string | undefined, now: KaiMo
152209
zkScheme,
153210
canonicalHash,
154211
canonicalHashOk,
155-
text: text || decodeProphecyText(parsed.textDecoded ?? "", payload.textEnc),
212+
text: text || decodeProphecyText(textDecoded, payload.textEnc),
156213
windowStatus,
157214
});
158215
}

src/SigilMarkets/state/marketStore.tsx

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,35 +1043,42 @@ export const SigilMarketsMarketProvider = (props: Readonly<{ children: ReactNode
10431043
);
10441044
};
10451045

1046-
const resolveMarket = (args: Readonly<{ marketId: MarketId; resolution: BinaryMarketState["resolution"] }>): void => {
1047-
if (!args.resolution) return;
1048-
const key = args.marketId as unknown as string;
1049-
setAndMaybePersist(
1050-
(prev) => {
1051-
const existing = prev.byId[key];
1052-
if (!existing) return prev;
1053-
const nextStatus = args.resolution.outcome === "VOID" ? "voided" : "resolved";
1054-
const resolvedPulse = args.resolution.resolvedPulse;
1055-
const nextMarket: Market = {
1056-
...existing,
1057-
state: {
1058-
...existing.state,
1059-
status: nextStatus,
1060-
resolution: args.resolution,
1061-
updatedPulse: Math.max(existing.state.updatedPulse, resolvedPulse),
1062-
},
1063-
};
1064-
return {
1065-
...prev,
1066-
byId: {
1067-
...prev.byId,
1068-
[key]: nextMarket,
1069-
},
1070-
};
1046+
const resolveMarket = (args: Readonly<{ marketId: MarketId; resolution: BinaryMarketState["resolution"] }>): void => {
1047+
const resolution = args.resolution; // ✅ capture once so TS can narrow for the closure
1048+
if (!resolution) return;
1049+
1050+
const key = args.marketId as unknown as string;
1051+
1052+
setAndMaybePersist(
1053+
(prev) => {
1054+
const existing = prev.byId[key];
1055+
if (!existing) return prev;
1056+
1057+
const nextStatus: MarketStatus = resolution.outcome === "VOID" ? "voided" : "resolved";
1058+
const resolvedPulse = resolution.resolvedPulse;
1059+
1060+
const nextMarket: Market = {
1061+
...existing,
1062+
state: {
1063+
...existing.state,
1064+
status: nextStatus,
1065+
resolution, // ✅ safe
1066+
updatedPulse: Math.max(existing.state.updatedPulse, resolvedPulse),
10711067
},
1072-
true,
1073-
);
1074-
};
1068+
};
1069+
1070+
return {
1071+
...prev,
1072+
byId: {
1073+
...prev.byId,
1074+
[key]: nextMarket,
1075+
},
1076+
};
1077+
},
1078+
true,
1079+
);
1080+
};
1081+
10751082

10761083
const setStatus = (status: MarketStoreStatus, error?: string): void => {
10771084
setState((prev) => ({ ...prev, status, error }));

0 commit comments

Comments
 (0)