Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement WorldApp global var check #39

Merged
merged 3 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions demo/with-next/components/ClientContent/Versions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"use client";

import {
MiniKit,
MiniKitInstallErrorCode,
MiniKitInstallErrorMessage,
} from "@worldcoin/minikit-js";

export const Versions = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andy-t-wang

Result

Desktop:
image

From the app:

image

image

const isValid = () => {
if (
typeof window === "undefined" ||
typeof window.WorldApp === "undefined"
) {
return { isValid: false, error: "window.WorldApp is undefined" };
}

try {
// @ts-ignore
if (MiniKit.commandsValid(window.WorldApp?.supported_commands)) {
return { isValid: true };
} else {
return {
isValid: false,
error:
MiniKitInstallErrorMessage[MiniKitInstallErrorCode.AppOutOfDate],
};
}
} catch (error) {
return {
isValid: false,
error: "Something went wrong on version validation",
};
}
};

return (
<div className="grid gap-y-4">
<h2 className="font-bold text-2xl">Versions</h2>

<div>
<p>window.WorldApp:</p>

<div className="bg-gray-300 min-h-[100px] p-2">
<pre
suppressHydrationWarning
className="break-all whitespace-break-spaces"
>
{JSON.stringify(window?.WorldApp ?? null, null, 2)}
</pre>
</div>
</div>

<div>
<p>Is versions Valid:</p>

<div className="bg-gray-300 min-h-[100px] p-2">
<pre className="break-all whitespace-break-spaces">
{JSON.stringify(isValid() ?? null, null, 2)}
</pre>
</div>
</div>
</div>
);
};
8 changes: 8 additions & 0 deletions demo/with-next/components/ClientContent/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import { User } from "./User";
import { Nav } from "./Nav";
import { WalletAuth } from "./WalletAuth";
import { ExternalLinks } from "./ExternalLinks";
import dynamic from "next/dynamic";

const VersionsNoSSR = dynamic(
() => import("./Versions").then((comp) => comp.Versions),
{ ssr: false }
);

export const ClientContent = () => {
return (
Expand All @@ -18,6 +24,8 @@ export const ClientContent = () => {
<hr />

<div className="grid gap-y-8">
<VersionsNoSSR />
Copy link
Contributor Author

@igorosip0v igorosip0v May 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andy-t-wang Made it dynamic import because in the demo we are using window.WorldApp inside the JSX markup, and nextjs still trying to hydrate this component end it cause hydration error, text inside <pre*>window.WorldApp</pre*> is not matches between server and client because window is not exist on server

<hr />
<VerifyAction />
<hr />
<Pay />
Expand Down
25 changes: 25 additions & 0 deletions demo/with-next/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interface Window {
webkit?: {
messageHandlers?: {
minikit?: {
postMessage?: (payload: Record<string, any>) => void;
};
};
};

Android?: {
postMessage?: (payload: string) => void;
};

MiniKit?: import("@worldcoin/minikit-js").MiniKit;

WorldApp?: {
world_app_version: number;
device_os: "ios" | "android";

supported_commands: Array<{
name: import("@worldcoin/minikit-js").Command;
supported_versions: Array<number>;
}>;
};
}
10 changes: 10 additions & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,14 @@ interface Window {
};

MiniKit?: import("./minikit").MiniKit;

WorldApp?: {
world_app_version: number;
device_os: "ios" | "android";

supported_commands: Array<{
name: import("./types/commands").Command;
supported_versions: Array<number>;
}>;
};
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export {
PaymentErrorMessage,
WalletAuthErrorCodes,
WalletAuthErrorMessage,
MiniKitInstallErrorCode,
MiniKitInstallErrorMessage,
} from "types/errors";

export { MiniKit } from "./minikit";
Expand All @@ -37,5 +39,4 @@ export {

export { Tokens, Network, TokenDecimals } from "./types/payment";
export { tokenToDecimals } from "helpers/payment/client";

export { VerificationLevel } from "@worldcoin/idkit-core";
85 changes: 74 additions & 11 deletions src/minikit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import {
import { VerificationLevel } from "@worldcoin/idkit-core";
import { validateWalletAuthCommandInput } from "helpers/siwe/validate-wallet-auth-command-input";
import { generateSiweMessage } from "helpers/siwe/siwe";
import {
MiniKitInstallErrorCode,
MiniKitInstallReturnType,
MiniKitInstallErrorMessage,
} from "types";

export const sendMiniKitEvent = <
T extends WebViewBasePayload = WebViewBasePayload,
Expand All @@ -28,12 +33,27 @@ export const sendMiniKitEvent = <
};

export class MiniKit {
private static readonly MINIKIT_VERSION = 1;

private static readonly commandVersion = {
[Command.Verify]: 1,
[Command.Pay]: 1,
[Command.WalletAuth]: 1,
};

private static listeners: Record<ResponseEvent, EventHandler> = {
[ResponseEvent.MiniAppVerifyAction]: () => {},
[ResponseEvent.MiniAppPayment]: () => {},
[ResponseEvent.MiniAppWalletAuth]: () => {},
};

private static sendInit() {
sendWebviewEvent({
command: "init",
payload: { version: this.MINIKIT_VERSION },
});
}

public static subscribe<E extends ResponseEvent>(
event: E,
handler: EventHandler<E>
Expand All @@ -54,14 +74,57 @@ export class MiniKit {
this.listeners[event](payload);
}

public static install() {
if (typeof window !== "undefined" && !Boolean(window.MiniKit)) {
try {
window.MiniKit = MiniKit;
} catch (error) {
console.error("Failed to install MiniKit", error);
return { success: false, error };
}
private static commandsValid(
input: NonNullable<typeof window.WorldApp>["supported_commands"]
) {
return input.every((command) =>
command.supported_versions.includes(this.commandVersion[command.name])
);
}

public static install(): MiniKitInstallReturnType {
if (typeof window === "undefined" || Boolean(window.MiniKit)) {
return {
success: false,
errorCode: MiniKitInstallErrorCode.AlreadyInstalled,
errorMessage:
MiniKitInstallErrorMessage[MiniKitInstallErrorCode.AlreadyInstalled],
};
}

if (!window.WorldApp) {
return {
success: false,
errorCode: MiniKitInstallErrorCode.OutsideOfWorldApp,
errorMessage:
MiniKitInstallErrorMessage[MiniKitInstallErrorCode.OutsideOfWorldApp],
};
}

if (!this.commandsValid(window.WorldApp.supported_commands)) {
return {
success: false,
errorCode: MiniKitInstallErrorCode.AppOutOfDate,
errorMessage:
MiniKitInstallErrorMessage[MiniKitInstallErrorCode.AppOutOfDate],
};
}

try {
window.MiniKit = MiniKit;
this.sendInit();
} catch (error) {
console.error(
MiniKitInstallErrorMessage[MiniKitInstallErrorCode.Unknown],
error
);

return {
success: false,
errorCode: MiniKitInstallErrorCode.Unknown,
errorMessage:
MiniKitInstallErrorMessage[MiniKitInstallErrorCode.Unknown],
};
}

return { success: true };
Expand All @@ -83,7 +146,7 @@ export class MiniKit {
};
sendMiniKitEvent({
command: Command.Verify,
version: 1,
version: this.commandVersion[Command.Verify],
payload: eventPayload,
});

Expand Down Expand Up @@ -113,7 +176,7 @@ export class MiniKit {

sendMiniKitEvent<WebViewBasePayload>({
command: Command.Pay,
version: 1,
version: this.commandVersion[Command.Pay],
payload: eventPayload,
});

Expand Down Expand Up @@ -168,7 +231,7 @@ export class MiniKit {

sendMiniKitEvent<WebViewBasePayload>({
command: Command.WalletAuth,
version: 1,
version: this.commandVersion[Command.WalletAuth],
payload: walletAuthPayload,
});

Expand Down
9 changes: 9 additions & 0 deletions src/types/commands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IDKitConfig, VerificationLevel } from "@worldcoin/idkit-core/*";
import { Network, Tokens } from "./payment";
import { MiniKitInstallErrorCode, MiniKitInstallErrorMessage } from "./";

export enum Command {
Verify = "verify",
Expand Down Expand Up @@ -47,3 +48,11 @@ export type PayCommandPayload = PayCommandInput & {
export type WalletAuthPayload = {
siweMessage: string;
};

export type MiniKitInstallReturnType =
| { success: true }
| {
success: false;
errorCode: MiniKitInstallErrorCode;
errorMessage: (typeof MiniKitInstallErrorMessage)[MiniKitInstallErrorCode];
};
18 changes: 18 additions & 0 deletions src/types/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,21 @@ export const WalletAuthErrorMessage = {
"Provided parameters in the request are invalid.",
[WalletAuthErrorCodes.UserRejected]: "User rejected the request.",
};

export enum MiniKitInstallErrorCode {
Unknown = "unknown",
AlreadyInstalled = "already_installed",
OutsideOfWorldApp = "outside_of_worldapp",
andy-t-wang marked this conversation as resolved.
Show resolved Hide resolved
NotOnClient = "not_on_client",
AppOutOfDate = "app_out_of_date",
}

export const MiniKitInstallErrorMessage = {
[MiniKitInstallErrorCode.Unknown]: "Failed to install MiniKit.",
[MiniKitInstallErrorCode.AlreadyInstalled]: "MiniKit is already installed.",
[MiniKitInstallErrorCode.OutsideOfWorldApp]:
"MiniApp launched outside of WorldApp.",
[MiniKitInstallErrorCode.NotOnClient]: "Window object is not available.",
[MiniKitInstallErrorCode.AppOutOfDate]:
"WorldApp is out of date. Please update the app.",
};
3 changes: 2 additions & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export {
PayCommandInput,
WebViewBasePayload,
Command,
MiniKitInstallReturnType,
} from "./commands";

export {
Expand All @@ -13,5 +14,5 @@ export {
} from "./responses";

export { Tokens } from "./payment";

export { SiweMessage } from "./wallet-auth";
export { MiniKitInstallErrorCode, MiniKitInstallErrorMessage } from "./errors";
Loading