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
9 changes: 8 additions & 1 deletion mintro-mini-app/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,12 @@ AUTH_URL=
WLD_CLIENT_ID=
NEXT_PUBLIC_WLD_CLIENT_ID=
NEXT_PUBLIC_PRIVY_APP_ID=
NEXT_PUBLIC_RPC_URL
NEXT_PUBLIC_RPC_URL=
NEXT_PUBLIC_COINBASE_SECRET_KEY=
NEXT_PUBLIC_COINBASE_PUBLIC_KEY=
NEXT_PUBLIC_COINBASE_APP_ID=
COINBASE_API_KEY_NAME=




18 changes: 18 additions & 0 deletions mintro-mini-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@ This template is a way for you to quickly get started with authentication and ex
9. Continue to developer.worldcoin.org and make sure your app is connected to the right ngrok url
10. [Optional] For Verify and Send Transaction to work you need to do some more setup in the dev portal. The steps are outlined in the respective component files.

## Add Money Quick Action

This mini app now uses the World App's Add Money Quick Action instead of Coinbase Onramp. The "Fund Wallet" button will open the World App's bridge interface where users can:

- Add money to their World Wallet directly from exchanges like Binance and Coinbase
- Deposit, withdraw, and swap tokens across multiple exchanges and chains
- Support for USDC and WLD tokens

The integration uses the following parameters:

- `app_id`: Your World App mini app ID
- `path`: URL-encoded path to the bridge interface (`%2Fbridge`)
- `toAddress`: The user's World Wallet address
- `toToken`: Token contract address (WLD: `0x16345785d8a0000`)
- `sourceAppId`: Your app ID for navigation back
- `sourceAppName`: "Mintro" (your app name)
- `sourceDeeplinkPath`: URL-encoded path back to your app (`%2Fhome`)

### Environment Variables

Create a `.env.local` file with the following variables:
Expand Down
5 changes: 4 additions & 1 deletion mintro-mini-app/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ const nextConfig: NextConfig = {
images: {
domains: ["static.usernames.app-backend.toolsforhumanity.com"],
},
allowedDevOrigins: ["https://mintro-two.vercel.app"], // Add your production origin here
allowedDevOrigins: [
"https://mintro-two.vercel.app",
"https://5758-83-144-23-154.ngrok-free.app",
], // Add your production and dev origins here
reactStrictMode: false,
};

Expand Down
50 changes: 31 additions & 19 deletions mintro-mini-app/src/app/(protected)/home/page.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
"use client";

import { useWorldcoinAuth } from "@/hooks/useWorldcoinAuth";
import { Page } from "@/components/PageLayout";
import { LogoutButton } from "@/components/LogoutButton";
import { Pay } from "@/components/Pay";
import { Transaction } from "@/components/Transaction";
import { UserInfo } from "@/components/UserInfo";
import { Verify } from "@/components/Verify";
// import { Verify } from "@/components/Verify";
import { ViewPermissions } from "@/components/ViewPermissions";
import { WalletBalance } from "@/components/WalletBalance";
// import { WalletBalance } from "@/components/WalletBalance";
import { Marble, TopBar } from "@worldcoin/mini-apps-ui-kit-react";
import { useWorldcoinAuth } from "@/hooks/useWorldcoinAuth";
import { useCallback } from "react";

export default function Home() {
const { user, isLoading } = useWorldcoinAuth();
export default function ProtectedHome() {
const { user } = useWorldcoinAuth();

if (isLoading) {
return (
<Page>
<Page.Main className="flex items-center justify-center">
<div className="text-center">
<p className="text-lg">Loading...</p>
</div>
</Page.Main>
</Page>
);
}
const fundWallet = useCallback(async () => {
if (!user?.address) return;
try {
const response = await fetch("/api/onramp", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
address: user.address,
amountUsd: "100", // Optional: you can make this configurable
}),
});
const { url } = await response.json();
window.open(url, "_blank");
} catch (e) {
console.error(e);
}
}, [user]);

return (
<>
Expand All @@ -42,9 +48,15 @@ export default function Home() {
/>
</Page.Header>
<Page.Main className="flex flex-col items-center justify-start gap-4 mb-16">
<button
onClick={fundWallet}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 mb-2"
>
Fund Wallet
</button>
<UserInfo />
<WalletBalance />
<Verify />
{/* <WalletBalance /> */}
{/* <Verify /> */}
<Pay />
<Transaction />
<ViewPermissions />
Expand Down
58 changes: 58 additions & 0 deletions mintro-mini-app/src/app/api/onramp/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
try {
const { address, amountUsd } = await req.json();

if (!address) {
return NextResponse.json(
{ error: "Address is required" },
{ status: 400 }
);
}

// World App Add Money Quick Action parameters
const appId = "app_e7d27c5ce2234e00558776f227f791ef";
const path = "/"; // URL-encoded path to the bridge interface
const toAddress = address;
const toToken = "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1"; // UDSC token contract address on World Chain
const sourceAppId = process.env.NEXT_PUBLIC_WLD_CLIENT_ID;
const sourceAppName = "Mintro"; // Your app name
const sourceDeeplinkPath = "/home"; // URL-encoded path back to your app

// Build the World App Add Money URL
const baseUrl = "https://worldcoin.org/mini-app";
const params = new URLSearchParams({
app_id: appId || "",
path,
toAddress,
toToken,
sourceAppId: sourceAppId || "",
sourceAppName,
sourceDeeplinkPath,
});

// Add amount if provided
if (amountUsd) {
params.append("amountUsd", amountUsd.toString());
}

const addMoneyUrl = `${baseUrl}?${params.toString()}`;

console.log("=== WORLD APP ADD MONEY URL ===");
console.log("App ID:", appId);
console.log("To Address:", toAddress);
console.log("To Token:", toToken);
console.log("Amount USD:", amountUsd);
console.log("Full URL:", addMoneyUrl);
console.log("=== END WORLD APP ADD MONEY URL ===");

return NextResponse.json({ url: addMoneyUrl });
} catch (error) {
console.error("Error creating World App Add Money URL:", error);
return NextResponse.json(
{ error: "Failed to create add money URL" },
{ status: 500 }
);
}
}
105 changes: 87 additions & 18 deletions mintro-mini-app/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,96 @@
"use client";
import { useWorldcoinAuth } from "@/hooks/useWorldcoinAuth";
import { Page } from "@/components/PageLayout";
import { AuthButton } from "../components/AuthButton";
import { AuthStatus } from "../components/AuthStatus";
import { MintroBranding } from "../components/MintroBranding";
import { DebugInfo } from "../components/DebugInfo";
import { LogoutButton } from "@/components/LogoutButton";
// import { Pay } from "@/components/Pay";
// import { Transaction } from "@/components/Transaction";
import { UserInfo } from "@/components/UserInfo";
// import { Verify } from "@/components/Verify";
// import { ViewPermissions } from "@/components/ViewPermissions";
// import { WalletBalance } from "@/components/WalletBalance";
import { Marble, TopBar } from "@worldcoin/mini-apps-ui-kit-react";
import { AuthButton } from "@/components/AuthButton";
import { useCallback } from "react";

export default function Home() {
return (
<Page>
<Page.Main className="flex flex-col gap-8 py-8">
{/* Mintro Branding and Timeline */}
<MintroBranding />
const { user, isLoading, isAuthenticated } = useWorldcoinAuth();

const fundWallet = useCallback(async () => {
if (!user?.address) return;
try {
const response = await fetch("/api/onramp", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
address: user.address,
amountUsd: "100", // Optional: you can make this configurable
}),
});
const { url } = await response.json();
window.open(url, "_blank");
} catch (e) {
console.error(e);
}
}, [user]);

if (isLoading) {
return (
<Page>
<Page.Main className="flex items-center justify-center">
<div className="text-center">
<p className="text-lg">Loading...</p>
</div>
</Page.Main>
</Page>
);
}

{/* Authentication Section */}
<div className="max-w-2xl mx-auto w-full space-y-6">
<AuthStatus />
<div className="flex justify-center">
<AuthButton />
if (!isAuthenticated) {
return (
<Page>
<Page.Main className="flex flex-col gap-8 py-8">
<div className="max-w-2xl mx-auto w-full space-y-6">
<div className="flex justify-center">
<AuthButton />
</div>
</div>
</div>
</Page.Main>
</Page>
);
}

return (
<>
<Page.Header className="p-0">
<TopBar
title="Home"
endAdornment={
<div className="flex items-center gap-2">
<p className="text-sm font-semibold capitalize">
{user?.username || "User"}
</p>
<Marble src={user?.profilePictureUrl} className="w-12" />
</div>
}
/>
</Page.Header>
<Page.Main className="flex flex-col items-center justify-start gap-4 mb-16">
<UserInfo />
<button
onClick={fundWallet}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 mb-2"
>
Fund Wallet
</button>

{/* Debug Information - Always at the bottom */}
<DebugInfo />
{/* <UserInfo /> */}
{/* <WalletBalance /> */}
{/* <Verify /> */}
{/* <Pay /> */}
{/* <Transaction />
<ViewPermissions /> */}
<LogoutButton />
</Page.Main>
</Page>
</>
);
}
5 changes: 2 additions & 3 deletions mintro-mini-app/src/components/AuthButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,8 @@ export const AuthButton = () => {
payload: result.finalPayload,
})
);

// Redirect to home page
router.push("/home");
window.dispatchEvent(new Event("worldcoin_auth_update"));
// router.push("/protected/home");
} catch (error) {
console.error("Worldcoin wallet authentication error", error);
} finally {
Expand Down
4 changes: 1 addition & 3 deletions mintro-mini-app/src/components/LogoutButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

import { Button } from "@worldcoin/mini-apps-ui-kit-react";
import { useWorldcoinAuth } from "@/hooks/useWorldcoinAuth";
import { useRouter } from "next/navigation";

export const LogoutButton = () => {
const { logout } = useWorldcoinAuth();
const router = useRouter();

const handleLogout = async () => {
try {
logout();
router.push("/");
window.location.reload(); // Soft reload, resets all state
} catch (error) {
console.error("Logout error:", error);
}
Expand Down
Loading