@worldcoin/minikit-js
Version:
minikit-js is our SDK for building mini-apps.
613 lines (608 loc) • 19 kB
JavaScript
import {
EventManager,
MiniKitInstallErrorMessage,
attestation,
chat,
closeMiniApp,
getPermissions,
pay,
requestPermission,
sendHapticFeedback,
sendTransaction,
share,
shareContacts,
signMessage,
signTypedData,
walletAuth
} from "./chunk-AH3WTRZY.js";
import {
isInWorldApp,
sendMiniKitEvent,
validateCommands
} from "./chunk-DZ2YRSIU.js";
// src/helpers/microphone.ts
var microphoneSetupDone = false;
var setupMicrophone = () => {
if (microphoneSetupDone) {
return;
}
if (typeof navigator !== "undefined" && !navigator.mediaDevices?.getUserMedia)
return;
const originalStop = MediaStreamTrack.prototype.stop;
MediaStreamTrack.prototype.stop = function() {
originalStop.call(this);
if (this.readyState === "ended") {
setTimeout(() => this.dispatchEvent(new Event("ended")), 0);
}
};
const realGUM = navigator.mediaDevices.getUserMedia.bind(
navigator.mediaDevices
);
const live = /* @__PURE__ */ new Set();
async function wrapped(constraints) {
const stream = await realGUM(constraints);
const hasAudioTrack = stream.getAudioTracks().length > 0;
if (hasAudioTrack) {
sendMiniKitEvent({
command: "microphone-stream-started",
version: 1,
payload: {
streamId: stream.id
}
});
live.add(stream);
stream.getAudioTracks().forEach((t) => {
t.addEventListener("ended", () => {
const allAudioTracksEnded = stream.getAudioTracks().every((track) => track.readyState === "ended");
if (allAudioTracksEnded) {
sendMiniKitEvent({
command: "microphone-stream-ended",
version: 1,
payload: {
streamId: stream.id
}
});
live.delete(stream);
}
});
});
}
return stream;
}
Object.defineProperty(navigator.mediaDevices, "getUserMedia", {
value: wrapped,
writable: false,
configurable: false,
enumerable: true
});
Object.freeze(navigator.mediaDevices);
const stopAllMiniAppMicrophoneStreams = () => {
live.forEach((s) => {
const audioTracks = s.getAudioTracks();
if (audioTracks.length > 0) {
audioTracks.forEach((t) => {
t.stop();
});
sendMiniKitEvent({
command: "microphone-stream-ended",
version: 1,
payload: {
streamId: s.id
}
});
}
});
live.clear();
};
MiniKit.subscribe("miniapp-microphone" /* MiniAppMicrophone */, (payload) => {
if (payload.status === "error" && (payload.error_code === "mini_app_permission_not_enabled" /* MiniAppPermissionNotEnabled */ || payload.error_code === "world_app_permission_not_enabled" /* WorldAppPermissionNotEnabled */)) {
console.log("stopping all microphone streams", payload);
stopAllMiniAppMicrophoneStreams();
}
});
window.__stopAllMiniAppMicrophoneStreams = stopAllMiniAppMicrophoneStreams;
microphoneSetupDone = true;
};
// src/helpers/usernames.ts
var getUserProfile = async (address) => {
const res = await fetch("https://usernames.worldcoin.org/api/v1/query", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
addresses: [address]
})
});
const usernames = await res.json();
return usernames?.[0] ?? { username: null, profile_picture_url: null };
};
// src/minikit.ts
var MINIKIT_VERSION = 1;
var MINIKIT_MINOR_VERSION = 96;
var WORLD_APP_LAUNCH_LOCATION_MAP = {
"app-store": "app-store" /* AppStore */,
carousel: "app-store" /* AppStore */,
explore: "app-store" /* AppStore */,
app_details: "app-store" /* AppStore */,
deeplink: "deep-link" /* DeepLink */,
homepage: "home" /* Home */,
wallet_tab: "wallet-tab" /* WalletTab */,
world_chat: "chat" /* Chat */
};
function mapWorldAppLaunchLocation(location) {
if (!location) return null;
const raw = typeof location === "object" && "open_origin" in location ? location.open_origin : typeof location === "string" ? location : null;
if (!raw) return null;
console.log("MiniKit launch location mapped:", raw);
return WORLD_APP_LAUNCH_LOCATION_MAP[raw.toLowerCase()] ?? null;
}
var _MiniKit = class _MiniKit {
static getActiveMiniKit() {
if (typeof window === "undefined") return this;
const candidate = window.MiniKit;
if (candidate && typeof candidate.trigger === "function") {
return candidate;
}
return this;
}
// ============================================================================
// Unified API (auto-detects environment)
// ============================================================================
/**
* Authenticate user via wallet signature (SIWE)
*
* Works in World App (native SIWE) and web (Wagmi + SIWE fallback).
*
* @example
* ```typescript
* const result = await MiniKit.walletAuth({ nonce: 'randomnonce123' });
* console.log(result.data.address);
* console.log(result.executedWith); // 'minikit' | 'wagmi' | 'fallback'
* ```
*/
static walletAuth(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.walletAuth(options);
}
return walletAuth(options, this.getContext());
}
/**
* Send one or more transactions
*
* World App: batch + permit2 + gas sponsorship
* Web: sequential execution via Wagmi
*
* @example
* ```typescript
* const result = await MiniKit.sendTransaction({
* chainId: 480,
* transactions: [{
* to: '0x...',
* data: '0x...',
* value: '0x0',
* }],
* });
* ```
*/
static sendTransaction(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.sendTransaction(options);
}
return sendTransaction(options, this.getContext());
}
/**
* Send a payment (World App only)
*
* Requires custom fallback on web.
*
* @example
* ```typescript
* const result = await MiniKit.pay({
* reference: crypto.randomUUID(),
* to: '0x...',
* tokens: [{ symbol: Tokens.WLD, token_amount: '1.0' }],
* description: 'Payment for coffee',
* fallback: () => showStripeCheckout(),
* });
* ```
*/
static pay(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.pay(options);
}
return pay(options, this.getContext());
}
/**
* Open the contact picker (World App only)
*
* Requires custom fallback on web.
*
* @example
* ```typescript
* const result = await MiniKit.shareContacts({
* isMultiSelectEnabled: true,
* fallback: () => showManualAddressInput(),
* });
* ```
*/
static shareContacts(options = {}) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.shareContacts(options);
}
return shareContacts(options, this.getContext());
}
/**
* Sign a message
*/
static signMessage(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.signMessage(options);
}
return signMessage(options, this.getContext());
}
/**
* Sign typed data (EIP-712)
*/
static signTypedData(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.signTypedData(options);
}
return signTypedData(options, this.getContext());
}
/**
* Send a chat message
*/
static chat(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.chat(options);
}
return chat(options, this.getContext());
}
/**
* Share files/text/URL
*/
static share(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.share(options);
}
return share(options, this.getContext());
}
/**
* Get current permission settings
*/
static getPermissions(options = {}) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.getPermissions(options);
}
return getPermissions(options, this.getContext());
}
/**
* Request a permission from the user
*/
static requestPermission(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.requestPermission(options);
}
return requestPermission(options, this.getContext());
}
/**
* Trigger haptic feedback
*/
static sendHapticFeedback(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.sendHapticFeedback(options);
}
return sendHapticFeedback(options, this.getContext());
}
/**
* Request app attestation token for a request hash
*/
static attestation(options) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.attestation(options);
}
return attestation(options, this.getContext());
}
/**
* Close the mini app
*/
static closeMiniApp(options = {}) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.closeMiniApp(options);
}
return closeMiniApp(options, this.getContext());
}
// ============================================================================
// Public State Accessors
// ============================================================================
static get appId() {
return this._appId;
}
static set appId(value) {
this._appId = value;
}
static get user() {
return this._user;
}
static set user(value) {
this._user = value;
}
static get deviceProperties() {
return this._deviceProperties;
}
static get location() {
return this._location;
}
// ============================================================================
// Event System
// ============================================================================
static subscribe(event, handler) {
const active = this.getActiveMiniKit();
if (active !== this) {
active.subscribe(event, handler);
return;
}
if (event === "miniapp-wallet-auth" /* MiniAppWalletAuth */) {
const originalHandler = handler;
const wrappedHandler = async (payload) => {
if (payload.status === "success") {
await this.updateUserFromWalletAuth(payload.address);
}
originalHandler(payload);
};
this.eventManager.subscribe(event, wrappedHandler);
} else {
this.eventManager.subscribe(event, handler);
}
}
static unsubscribe(event) {
const active = this.getActiveMiniKit();
if (active !== this) {
active.unsubscribe(event);
return;
}
this.eventManager.unsubscribe(event);
}
static trigger(event, payload) {
const active = this.getActiveMiniKit();
if (active !== this) {
active.trigger(event, payload);
return;
}
this.eventManager.trigger(event, payload);
}
// ============================================================================
// Installation
// ============================================================================
static sendInit() {
sendMiniKitEvent({
command: "init",
payload: {
version: MINIKIT_VERSION,
minorVersion: MINIKIT_MINOR_VERSION
}
});
}
static install(appId) {
const active = this.getActiveMiniKit();
if (active !== this) {
return active.install(appId);
}
if (typeof window === "undefined") {
return {
success: false,
errorCode: "already_installed" /* AlreadyInstalled */,
errorMessage: MiniKitInstallErrorMessage["already_installed" /* AlreadyInstalled */]
};
}
if (!appId) {
console.warn("App ID not provided during install");
} else {
this._appId = appId;
}
if (!window.WorldApp) {
return {
success: false,
errorCode: "outside_of_worldapp" /* OutsideOfWorldApp */,
errorMessage: MiniKitInstallErrorMessage["outside_of_worldapp" /* OutsideOfWorldApp */]
};
}
this.initFromWorldApp(window.WorldApp);
try {
window.MiniKit = this;
this.sendInit();
} catch (error) {
console.error(
MiniKitInstallErrorMessage["unknown" /* Unknown */],
error
);
return {
success: false,
errorCode: "unknown" /* Unknown */,
errorMessage: MiniKitInstallErrorMessage["unknown" /* Unknown */]
};
}
this._isReady = true;
setupMicrophone();
if (!validateCommands(window.WorldApp.supported_commands)) {
return {
success: false,
errorCode: "app_out_of_date" /* AppOutOfDate */,
errorMessage: MiniKitInstallErrorMessage["app_out_of_date" /* AppOutOfDate */]
};
}
return { success: true };
}
static isInstalled(debug) {
const isInstalled = this._isReady && Boolean(window.MiniKit);
if (!isInstalled) {
console.warn(
"MiniKit is not installed. Make sure you're running the application inside of World App"
);
}
if (debug && isInstalled) {
console.log("MiniKit is alive!");
}
return isInstalled;
}
// ============================================================================
// Internal
// ============================================================================
static initFromWorldApp(worldApp) {
if (!worldApp) return;
this._user = {};
this._deviceProperties = {};
this._user.optedIntoOptionalAnalytics = worldApp.is_optional_analytics;
this._user.preferredCurrency = worldApp.preferred_currency;
this._user.pendingNotifications = worldApp.pending_notifications;
this._user.walletAddress = worldApp.wallet_address;
if (worldApp.verification_status) {
this._user.verificationStatus = {
isOrbVerified: worldApp.verification_status.is_orb_verified,
isDocumentVerified: worldApp.verification_status.is_document_verified,
isSecureDocumentVerified: worldApp.verification_status.is_secure_document_verified
};
}
this._deviceProperties.safeAreaInsets = worldApp.safe_area_insets;
this._deviceProperties.deviceOS = worldApp.device_os;
this._deviceProperties.worldAppVersion = worldApp.world_app_version;
this._location = mapWorldAppLaunchLocation(worldApp.location);
}
static async updateUserFromWalletAuth(address) {
this._user.walletAddress = address;
try {
const userProfile = await getUserProfile(address);
this._user.username = userProfile.username;
this._user.profilePictureUrl = userProfile.profile_picture_url;
} catch (error) {
console.error("Failed to fetch user profile:", error);
}
}
static getContext() {
return {
events: this.eventManager,
state: { deviceProperties: this._deviceProperties }
};
}
// ============================================================================
// Deprecated — remove in next major
// ============================================================================
/**
* @deprecated Use `MiniKit.pay()`, `MiniKit.walletAuth()`, etc. directly.
*
* Migration guide:
* - `MiniKit.commands.pay(payload)` → `await MiniKit.pay(options)`
* - `MiniKit.commands.walletAuth(payload)` → `await MiniKit.walletAuth(options)`
* - `MiniKit.commands.sendTransaction(payload)` → `await MiniKit.sendTransaction(options)`
* - `MiniKit.commands.signMessage(payload)` → `await MiniKit.signMessage(input)`
* - `MiniKit.commands.signTypedData(payload)` → `await MiniKit.signTypedData(input)`
* - `MiniKit.commands.shareContacts(payload)` → `await MiniKit.shareContacts(options)`
* - `MiniKit.commands.chat(payload)` → `await MiniKit.chat(input)`
* - `MiniKit.commands.share(payload)` → `await MiniKit.share(input)`
* - `MiniKit.commands.getPermissions()` → `await MiniKit.getPermissions()`
* - `MiniKit.commands.requestPermission(payload)` → `await MiniKit.requestPermission(input)`
* - `MiniKit.commands.sendHapticFeedback(payload)` → `await MiniKit.sendHapticFeedback(input)`
* - `MiniKit.commands.attestation(payload)` → `await MiniKit.attestation(options)`
* - `MiniKit.commands.closeMiniApp()` → `await MiniKit.closeMiniApp()`
*/
static get commands() {
throw new Error(
"MiniKit.commands has been removed. Use MiniKit.pay(), MiniKit.walletAuth(), etc. directly."
);
}
/**
* @deprecated Use `MiniKit.pay()`, `MiniKit.walletAuth()`, etc. directly. All commands are now async by default.
*
* See `MiniKit.commands` deprecation notice for the full migration guide.
*/
static get commandsAsync() {
throw new Error(
"MiniKit.commandsAsync has been removed. Use MiniKit.pay(), MiniKit.walletAuth(), etc. directly."
);
}
};
_MiniKit.eventManager = new EventManager();
// State (was MiniKitState)
_MiniKit._appId = null;
_MiniKit._user = {};
_MiniKit._deviceProperties = {};
_MiniKit._location = null;
_MiniKit._isReady = false;
/**
* Check if running inside World App
*/
_MiniKit.isInWorldApp = isInWorldApp;
// ============================================================================
// Utility Methods
// ============================================================================
_MiniKit.getUserByAddress = async (address) => {
const walletAddress = address ?? _MiniKit._user.walletAddress;
const userProfile = await getUserProfile(walletAddress);
return {
walletAddress,
username: userProfile.username,
profilePictureUrl: userProfile.profile_picture_url
};
};
_MiniKit.getUserByUsername = async (username) => {
const res = await fetch(
`https://usernames.worldcoin.org/api/v1/${username}`,
{
method: "GET",
headers: {
"Content-Type": "application/json"
}
}
);
const user = await res.json();
return {
walletAddress: user.address,
username: user.username,
profilePictureUrl: user.profile_picture_url
};
};
_MiniKit.getUserInfo = _MiniKit.getUserByAddress;
_MiniKit.getMiniAppUrl = (appId, path) => {
const baseUrl = new URL("https://world.org/mini-app");
baseUrl.searchParams.append("app_id", appId);
if (path) {
const fullPath = path.startsWith("/") ? path : `/${path}`;
baseUrl.searchParams.append("path", encodeURIComponent(fullPath));
}
return baseUrl.toString();
};
_MiniKit.showProfileCard = (username, walletAddress) => {
if (!username && !walletAddress) {
console.error(
"Either username or walletAddress must be provided to show profile card"
);
return;
}
if (username) {
window.open(
`worldapp://profile?username=${encodeURIComponent(username)}`
);
} else {
window.open(
`worldapp://profile?address=${encodeURIComponent(walletAddress || "")}`
);
}
};
var MiniKit = _MiniKit;
export {
MiniKit
};