Skip to content
Open
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
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@
"mini-apps/more/releases",
"mini-apps/more/webview-spec",
"mini-apps/more/community-tools-perks",
"mini-apps/more/faq"
"mini-apps/more/faq",
"mini-apps/more/minikit-v2"
]
}
]
Expand Down
2 changes: 1 addition & 1 deletion mini-apps/commands/pay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ There are two ways to verify a payment:

### Developer Portal API

Use the [Get Transaction](/api-reference/get-transaction) endpoint to get the current status of the transaction. When the transaction has landed on-chain, the `transaction_status` will be `mined`.
Use the [Get Transaction](/mini-apps/reference/api#get-transaction) endpoint to get the current status of the transaction. When the transaction has landed on-chain, the `transaction_status` will be `mined`.

```tsx app/confirm-payment/route.ts
import { NextRequest, NextResponse } from 'next/server'
Expand Down
358 changes: 358 additions & 0 deletions mini-apps/more/minikit-v2.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,358 @@
---
title: "MiniKit v2.0"
"og:image": "/images/docs/docs-meta.png"
"twitter:image": "/images/docs/docs-meta.png"
---

<Note>MiniKit 2.0 is still in beta and shouldn't be used in production. Try it out here: [2.0.0-dev.0](https://www.npmjs.com/package/@worldcoin/minikit-js/v/2.0.0-dev.0)</Note>

## Summary

We are standardizing SDKs by concern:

- `@worldcoin/idkit` owns World ID verification APIs and UI.
- `@worldcoin/minikit-js` owns mini-app commands only, verify is moved out to IDKit.

## Why

Developers building on the World network should not have to choose between shipping a Mini App and a standalone application.

Historically, that choice created friction for three reasons:
- World ID verification logic differed between Mini Apps and IDKit.
- MiniKit command APIs were tightly coupled to World App runtime behavior.
- MiniKit v1 wallet interactions did not follow EIP-1193 conventions (for example, wagmi/viem patterns), so existing dApps often required significant refactors to adopt MiniKit-specific walletAuth and sendTransaction flows.

MiniKit v2 addresses this by:
- Removing verification APIs/UI from MiniKit and standardizing verification on IDKit.
- Allowing existing wagmi-based dApps to add MiniKitProvider so wallet/transaction behavior adapts automatically to World App context.
- Supporting custom fallback logic across commands, so apps behave consistently both inside and outside World App.

## Migration

### World ID moved completely to IDKit

**Old:** Only works in a Mini App context
```ts
// 1. Custom verification logic for MiniKit using World ID 3.0
await MiniKit.commandsAsync.verify({
action: 'my-action',
signal: 'user-123',
});

// 2. Verify Proof
await fetch('/api/verify-proof', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(completion.result),
});
```

**New:** Verification works as a mini app and standalone
```ts
import { IDKit, orbLegacy } from '@worldcoin/idkit';

// 1. NEW: Requests now require signing in 4.0 to ensure legitimacy of RP context.
const rpContext = await fetch('/api/rp-signature', { method: 'POST' }).then((r) =>
r.json(),
);
// 2. Request verification
const request = await IDKit.request({
app_id: process.env.NEXT_PUBLIC_APP_ID as `app_${string}`,
action: 'my-action',
rp_context: rpContext,
allow_legacy_proofs: false,
environment: 'production',
}).preset(orbLegacy({ signal: 'user-123' }));

const completion = await request.pollUntilCompletion();
// 3. Verify Proof
if (completion.success) {
await fetch('/api/verify-proof', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(completion.result),
});
}
```

### MiniKit Command API (v2 -> v3)

#### 1) `MiniKit.commands` / `MiniKit.commandsAsync` are removed

**Old:**

```ts
const payload = MiniKit.commands.signMessage({ message: 'hello' });
const { commandPayload, finalPayload } = await MiniKit.commandsAsync.walletAuth({
nonce,
});
```

**New:**

```ts
const signResult = await MiniKit.signMessage({ message: 'hello' });
const authResult = await MiniKit.walletAuth({ nonce });
```

#### 2) Type and helper exports moved to `@worldcoin/minikit-js/commands`
This reduces bundle size by importing types and helpers without pulling in the full MiniKit runtime.

**New:** Updated paths for types and helpers
```tsx
import type { MiniKitSendHapticFeedbackOptions } from '@worldcoin/minikit-js/commands'; // MOVED
import { getIsUserVerified } from '@worldcoin/minikit-js/address-book'; // MOVED
import { parseSiweMessage, verifySiweMessage } from '@worldcoin/minikit-js/siwe'; // MOVED

const options: MiniKitSendHapticFeedbackOptions = {
hapticsType: 'success',
};

```

#### 3) Commands now support custom fallbacks

Use `fallback` to keep the same command flow working outside World App.
Fallback by default expect a response with the same shape as the original command, but you can also override and return custom data.

```ts Fallback handlers for outside World App
const result = await MiniKit.sendHapticFeedback({
hapticsType: 'impact',
style: 'light',
fallback: () => { // NEW
navigator.vibrate?.(20);
return {
status: 'success',
version: 1,
timestamp: new Date().toISOString(),
};
},
});
```

#### 4) Return shape changed to `{ executedWith, data }`
Command responses now include `executedWith` to indicate whether the command was executed by `minikit` | `fallback` | `wagmi` (if applicable).
The actual command response data is still nested under `data`.

**Old:**
```ts Fails outside World App
const { finalPayload } = await MiniKit.commandsAsync.sendTransaction({
transaction: [tx],
});
console.log(finalPayload.transaction_id);
```
**New:**
```ts Custom handling based on execution context
const result = await MiniKit.sendTransaction({
transaction: [tx],
});

console.log(result.executedWith); // 'minikit' | 'wagmi' | 'fallback'
console.log(result.data.transactionId);
```

#### 5) `walletAuth` nonce validation is stricter
In order to be in line with EIP-4361, `walletAuth` now requires a nonce without hyphens. You can use `crypto.randomUUID().replace(/-/g, '')`.

**Old:**

```ts Nonce with hyphens
const nonce = crypto.randomUUID(); // contains hyphens
await MiniKit.commandsAsync.walletAuth({ nonce });
```

**New:**

```ts Nonce without hyphens
const nonce = crypto.randomUUID().replace(/-/g, '');
await MiniKit.walletAuth({ nonce });
```

#### 6) `sendTransaction` 2.0

Send Transaction will switch to [Permit2 Approves](https://github.com/Uniswap/permit2/blob/cc56ad0f3439c502c246fc5cfcc3db92bb8b7219/src/AllowanceTransfer.sol#L26-L30) with expiration time 0.
Apps that need longer standing approvals will be granted on a case by case basis. This will require a hard migration, but provides much more robust security and flexibility for standing approvals to developers.
Transactions themselves will now purely be calldata in line with `eth_sendTransaction`.
```ts
await MiniKit.sendTransaction({
transactions: [
{
data: // Calldata Hex String
value: // Hex encoded string
to: // Hex
},
],
chainId: 480
});
```
<Note>Backwards compatibility with v1 will be supported for a period of time, but we encourage developers to migrate to the new format as soon as possible.</Note>

Type changes:

```ts Transaction Types
type Transaction = {
address: string;
value?: string;
data?: string; // takes priority when present
};

interface MiniKitSendTransactionOptions<TCustomFallback = SendTransactionResult> {
transaction: Transaction[];
chainId?: number; // defaults to 480 on World App
}
```

## Running your Mini App as a Standalone App

#### 1) World ID
Migrating to World ID 4.0 with IDKit will allow your verification flow to work out of the box. You can
optionally add branching logic to show the IDKit widget

**Example:**
```tsx Example Verify Flow
export function VerifyHybrid() {
const [open, setOpen] = useState(false);
const [rpContext, setRpContext] = useState<RpContext | null>(null);
const [status, setStatus] = useState<string>('idle');

const onVerify = async () => {
setStatus('loading');
const rp = await fetchRpContext(ACTION);
// Add branching logic here to show Widget outside of World App
if (MiniKit.isInWorldApp()) {
// World App: headless/native path (no widget UI)
const req = await IDKit.request({
app_id: APP_ID,
action: ACTION,
rp_context: rp,
allow_legacy_proofs: true,
environment: ENV,
}).preset(orbLegacy({ signal: `user-${Date.now()}` }));

const completion = await req.pollUntilCompletion();
if (!completion.success) {
setStatus(`error:${completion.error}`);
return;
}

await verifyProof(completion.result);
setStatus('verified:native');
return;
}

// Standalone web: open widget
setRpContext(rp);
setOpen(true);
setStatus('widget-open');
};

return (
<>
<button onClick={onVerify}>Verify</button>
<p>{status}</p>

{rpContext ? (
<IDKitRequestWidget
open={open}
onOpenChange={setOpen}
app_id={APP_ID}
action={ACTION}
rp_context={rpContext}
allow_legacy_proofs={true}
preset={orbLegacy({ signal: `user-${Date.now()}` })}
environment={ENV}
onSuccess={async (result) => {
await verifyProof(result);
setStatus('verified:widget');
}}
onError={(code) => setStatus(`error:${code}`)}
/>
) : null}
</>
);
}
```
#### 2) Transactions
Adding Wagmi provider. While World App will automatically bundle transactions into `multiCall` most wallets will not do this. Thus you should consider wrapping your call in a multi call when outside of World App for best compatibility.
```tsx Install dependencies
pnpm add @worldcoin/minikit-js wagmi viem @tanstack/react-query
```

```tsx Add Wagmi Config
// src/providers/wagmi-config.ts
import { worldApp } from '@worldcoin/minikit-js/wagmi';
import { worldchain } from 'viem/chains';
import { http } from 'viem';
import { createConfig } from 'wagmi';
import { injected } from 'wagmi/connectors';

export const wagmiConfig = createConfig({
chains: [worldchain],
transports: {
[worldchain.id]: http('https://worldchain-mainnet.g.alchemy.com/public'),
},
connectors: [
worldApp(), // native in World App
injected(), // web fallback connector
],
});
```


```tsx Update providers

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MiniKitProvider } from '@worldcoin/minikit-js/provider';
import { WagmiProvider } from 'wagmi';
import { wagmiConfig } from './wagmi-config';

const queryClient = new QueryClient();

export function ClientProviders({ children }: { children: React.ReactNode }) {
return (
// NEW QueryClientProvider and WagmiProvider recommended for robust performance
<QueryClientProvider client={queryClient}>
<WagmiProvider config={wagmiConfig}>
<MiniKitProvider
props={{
appId: process.env.NEXT_PUBLIC_APP_ID ?? '',
wagmiConfig, // NEW: pass explicitly for robust fallback behavior
}}
>
{children}
</MiniKitProvider>
</WagmiProvider>
</QueryClientProvider>
);
}
```
Once these are set up your application will automatically support transaction logic externally without any refactoring to core command calls.
#### 3) Non Transaction Command fallbacks
MiniKit v2 is designed to work both inside and outside of World App. By adding custom fallbacks to commands, you can ensure your app behaves gracefully when users access it outside of World App.


## Standalone App to Mini App


#### 1) World ID
No changes, simply enable the mini app option under configuration in the developer portal.

#### 2) Transactions
**Wagmi**: Add `MiniKitProvider` to your root and use the `worldApp` provider from the examples in the [previous section](/mini-apps/more/minikit-v2#2-transactions)

If you're using instead using **Viem** or **Ethers**. Add a branch for World App and use the `getWorldAppProvider` helper to automatically
bridge your commands.
```tsx Viem/Ethers
import { MiniKit, getWorldAppProvider } from '@worldcoin/minikit-js'
import { BrowserProvider } from 'ethers' // or viem custom transport

const inWorldApp = MiniKit.isInWorldApp()

if (inWorldApp) {
const provider = new BrowserProvider(getWorldAppProvider())
const signer = await provider.getSigner()
// signer.sendTransaction / signMessage works via MiniKit bridge
} else {
// your normal viem/ethers provider flow
}
```
Loading