From e40c803f3a368bb5d1afbeb7683a9fdd66ee416a Mon Sep 17 00:00:00 2001 From: Paolo Antonio Rossi Date: Sat, 5 Jul 2025 21:20:05 +0200 Subject: [PATCH 1/7] privy: on ramp progress --- mintro-mini-app/.env.sample | 8 +- .../src/app/(protected)/home/page.tsx | 55 --------- mintro-mini-app/src/app/api/onramp/route.ts | 17 +++ mintro-mini-app/src/app/page.tsx | 109 +++++++++++++++--- .../src/components/AuthButton/index.tsx | 4 +- .../src/components/MintroBranding/index.tsx | 2 +- 6 files changed, 118 insertions(+), 77 deletions(-) delete mode 100644 mintro-mini-app/src/app/(protected)/home/page.tsx create mode 100644 mintro-mini-app/src/app/api/onramp/route.ts diff --git a/mintro-mini-app/.env.sample b/mintro-mini-app/.env.sample index fa2a79a..3ace568 100644 --- a/mintro-mini-app/.env.sample +++ b/mintro-mini-app/.env.sample @@ -5,5 +5,11 @@ 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= + + + diff --git a/mintro-mini-app/src/app/(protected)/home/page.tsx b/mintro-mini-app/src/app/(protected)/home/page.tsx deleted file mode 100644 index ee62686..0000000 --- a/mintro-mini-app/src/app/(protected)/home/page.tsx +++ /dev/null @@ -1,55 +0,0 @@ -"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 { ViewPermissions } from "@/components/ViewPermissions"; -import { WalletBalance } from "@/components/WalletBalance"; -import { Marble, TopBar } from "@worldcoin/mini-apps-ui-kit-react"; - -export default function Home() { - const { user, isLoading } = useWorldcoinAuth(); - - if (isLoading) { - return ( - - -
-

Loading...

-
-
-
- ); - } - - return ( - <> - - -

- {user?.username || "User"} -

- - - } - /> -
- - - - - - - - - - - ); -} diff --git a/mintro-mini-app/src/app/api/onramp/route.ts b/mintro-mini-app/src/app/api/onramp/route.ts new file mode 100644 index 0000000..fb3b61b --- /dev/null +++ b/mintro-mini-app/src/app/api/onramp/route.ts @@ -0,0 +1,17 @@ +import { NextRequest, NextResponse } from "next/server"; + +export async function POST(req: NextRequest) { + const { address, redirectUrl } = await req.json(); + + const baseUrl = "https://pay.coinbase.com/buy"; + const params = new URLSearchParams({ + appId: process.env.COINBASE_ONRAMP_PUBLIC_KEY!, + destinationWallets: JSON.stringify([ + { address, blockchains: ["ethereum"] }, + ]), + redirectUrl, + }); + + const onrampUrl = `${baseUrl}?${params.toString()}`; + return NextResponse.json({ url: onrampUrl }); +} diff --git a/mintro-mini-app/src/app/page.tsx b/mintro-mini-app/src/app/page.tsx index 8a95e32..6fb20b7 100644 --- a/mintro-mini-app/src/app/page.tsx +++ b/mintro-mini-app/src/app/page.tsx @@ -1,27 +1,100 @@ +"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 ( - - - {/* Mintro Branding and Timeline */} - + 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, + redirectUrl: window.location.href, + }), + }); + const { url } = await response.json(); + window.open(url, "_blank"); + } catch (e) { + console.error(e); + } + }, [user]); - {/* Authentication Section */} -
- -
- + if (isLoading) { + return ( + + +
+

Loading...

-
+ + + ); + } - {/* Debug Information - Always at the bottom */} - + if (!isAuthenticated) { + return ( + + +
+
+ +
+
+
+
+ ); + } + + return ( + <> + + +

+ {user?.username || "User"} +

+ +
+ } + /> + + + + + + + + + + + -
+ ); } diff --git a/mintro-mini-app/src/components/AuthButton/index.tsx b/mintro-mini-app/src/components/AuthButton/index.tsx index 2c95eb9..0c9a41f 100644 --- a/mintro-mini-app/src/components/AuthButton/index.tsx +++ b/mintro-mini-app/src/components/AuthButton/index.tsx @@ -80,8 +80,8 @@ export const AuthButton = () => { }) ); - // Redirect to home page - router.push("/home"); + // Redirect to protected home page + router.push("/protected/home"); } catch (error) { console.error("Worldcoin wallet authentication error", error); } finally { diff --git a/mintro-mini-app/src/components/MintroBranding/index.tsx b/mintro-mini-app/src/components/MintroBranding/index.tsx index 043511d..341406f 100644 --- a/mintro-mini-app/src/components/MintroBranding/index.tsx +++ b/mintro-mini-app/src/components/MintroBranding/index.tsx @@ -178,7 +178,7 @@ export const MintroBranding = () => { Mintro

- Your intelligent DeFi companion v0.13 + Your intelligent DeFi companion v0.15

From cbe969e83e7f10929e895b3d4b5187cb76b7ca81 Mon Sep 17 00:00:00 2001 From: Paolo Antonio Rossi Date: Sat, 5 Jul 2025 21:23:16 +0200 Subject: [PATCH 2/7] chore: remove eth balance check --- mintro-mini-app/src/app/api/onramp/route.ts | 8 +- .../src/components/MintroBranding/index.tsx | 78 +------------------ 2 files changed, 6 insertions(+), 80 deletions(-) diff --git a/mintro-mini-app/src/app/api/onramp/route.ts b/mintro-mini-app/src/app/api/onramp/route.ts index fb3b61b..32fb47e 100644 --- a/mintro-mini-app/src/app/api/onramp/route.ts +++ b/mintro-mini-app/src/app/api/onramp/route.ts @@ -5,10 +5,10 @@ export async function POST(req: NextRequest) { const baseUrl = "https://pay.coinbase.com/buy"; const params = new URLSearchParams({ - appId: process.env.COINBASE_ONRAMP_PUBLIC_KEY!, - destinationWallets: JSON.stringify([ - { address, blockchains: ["ethereum"] }, - ]), + appId: process.env.COINBASE_ONRAMP_APP_ID!, // Updated env var name + // New Coinbase Onramp parameters + addresses: JSON.stringify({ worldchain: address }), + assets: JSON.stringify(["WLD"]), redirectUrl, }); diff --git a/mintro-mini-app/src/components/MintroBranding/index.tsx b/mintro-mini-app/src/components/MintroBranding/index.tsx index 341406f..ff8e477 100644 --- a/mintro-mini-app/src/components/MintroBranding/index.tsx +++ b/mintro-mini-app/src/components/MintroBranding/index.tsx @@ -3,7 +3,7 @@ import { useWorldcoinAuth } from "@/hooks/useWorldcoinAuth"; import { Button } from "@worldcoin/mini-apps-ui-kit-react"; import { useState, useEffect } from "react"; -import { ethers } from "ethers"; +// Removed: import { ethers } from "ethers"; interface SwapNotification { id: string; @@ -87,20 +87,12 @@ const getTypeIcon = (type: string) => { } }; -const WLD_CONTRACT = "0x163f8C2467924be0ae7B5347228C0F3Fc0cC008e"; // WLD token contract on World Chain - needs checksum -const ERC20_ABI = [ - "function balanceOf(address owner) view returns (uint256)", - "function decimals() view returns (uint8)", -]; - export const MintroBranding = () => { const { isLoading, isAuthenticated, user } = useWorldcoinAuth(); const [formattedNotifications, setFormattedNotifications] = useState< SwapNotification[] >([]); const [isClient, setIsClient] = useState(false); - const [wldBalance, setWldBalance] = useState(null); - const [balanceError, setBalanceError] = useState(null); useEffect(() => { setIsClient(true); @@ -113,62 +105,6 @@ export const MintroBranding = () => { setFormattedNotifications(formatted); }, []); - useEffect(() => { - async function fetchWldBalance() { - if (!user?.address) { - console.log("No user address available"); - return; - } - - try { - console.log("Fetching WLD balance for address:", user.address); - - // Check if RPC URL is available - const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL; - if (!rpcUrl) { - throw new Error( - "NEXT_PUBLIC_RPC_URL environment variable is not set" - ); - } - - console.log("Using RPC URL:", rpcUrl); - console.log("Network: World Chain (Worldcoin's blockchain)"); - - const provider = new ethers.JsonRpcProvider(rpcUrl); - const checksummedAddress = ethers.getAddress(WLD_CONTRACT); - console.log("Contract address:", WLD_CONTRACT); - console.log("Checksummed address:", checksummedAddress); - const contract = new ethers.Contract( - checksummedAddress, - ERC20_ABI, - provider - ); - - const [rawBalance, decimals] = await Promise.all([ - contract.balanceOf(user.address), - contract.decimals(), - ]); - - console.log("Raw balance:", rawBalance.toString()); - console.log("Decimals:", decimals); - - const formattedBalance = ethers.formatUnits(rawBalance, decimals); - console.log("Formatted balance:", formattedBalance); - - setWldBalance(formattedBalance); - setBalanceError(null); - } catch (error) { - const errorMessage = - error instanceof Error ? error.message : "Unknown error"; - console.error("Error fetching WLD balance:", errorMessage); - setBalanceError(errorMessage); - setWldBalance(null); - } - } - - fetchWldBalance(); - }, [user?.address]); - return (
{/* Hero Section */} @@ -178,7 +114,7 @@ export const MintroBranding = () => { Mintro

- Your intelligent DeFi companion v0.15 + Your intelligent DeFi companion v0.17

@@ -242,16 +178,6 @@ export const MintroBranding = () => { ) : isAuthenticated ? (
- {wldBalance !== null && ( -
- WLD Balance: {wldBalance} -
- )} - {balanceError && ( -
- Error fetching balance: {balanceError} -
- )}
User Address: {user?.address}
From 7e281a271d5c6526ad15193bc73e4512e3679ee1 Mon Sep 17 00:00:00 2001 From: Paolo Antonio Rossi Date: Sat, 5 Jul 2025 21:41:57 +0200 Subject: [PATCH 3/7] frontend: fix login/logout --- .../src/app/(protected)/home/page.tsx | 67 +++++++++++++++++++ mintro-mini-app/src/app/page.tsx | 63 ++++++++--------- .../src/components/AuthButton/index.tsx | 2 +- .../src/components/LogoutButton/index.tsx | 4 +- 4 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 mintro-mini-app/src/app/(protected)/home/page.tsx diff --git a/mintro-mini-app/src/app/(protected)/home/page.tsx b/mintro-mini-app/src/app/(protected)/home/page.tsx new file mode 100644 index 0000000..8cf974d --- /dev/null +++ b/mintro-mini-app/src/app/(protected)/home/page.tsx @@ -0,0 +1,67 @@ +"use client"; +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 { ViewPermissions } from "@/components/ViewPermissions"; +// 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 ProtectedHome() { + const { user } = 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, + redirectUrl: window.location.href, + }), + }); + const { url } = await response.json(); + window.open(url, "_blank"); + } catch (e) { + console.error(e); + } + }, [user]); + + return ( + <> + + +

+ {user?.username || "User"} +

+ +
+ } + /> + + + + + {/* */} + {/* */} + + + + + + + ); +} diff --git a/mintro-mini-app/src/app/page.tsx b/mintro-mini-app/src/app/page.tsx index 6fb20b7..3c9e62c 100644 --- a/mintro-mini-app/src/app/page.tsx +++ b/mintro-mini-app/src/app/page.tsx @@ -2,36 +2,36 @@ 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 { 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 { 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"; +// import { useCallback } from "react"; export default function Home() { 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, - redirectUrl: window.location.href, - }), - }); - const { url } = await response.json(); - window.open(url, "_blank"); - } catch (e) { - console.error(e); - } - }, [user]); + // 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, + // redirectUrl: window.location.href, + // }), + // }); + // const { url } = await response.json(); + // window.open(url, "_blank"); + // } catch (e) { + // console.error(e); + // } + // }, [user]); if (isLoading) { return ( @@ -75,7 +75,8 @@ export default function Home() { /> - - - - - - - + */} + {/* */} + {/* */} + {/* */} + {/* */} + {/* + */} diff --git a/mintro-mini-app/src/components/AuthButton/index.tsx b/mintro-mini-app/src/components/AuthButton/index.tsx index 0c9a41f..7cd2e62 100644 --- a/mintro-mini-app/src/components/AuthButton/index.tsx +++ b/mintro-mini-app/src/components/AuthButton/index.tsx @@ -81,7 +81,7 @@ export const AuthButton = () => { ); // Redirect to protected home page - router.push("/protected/home"); + // router.push("/protected/home"); } catch (error) { console.error("Worldcoin wallet authentication error", error); } finally { diff --git a/mintro-mini-app/src/components/LogoutButton/index.tsx b/mintro-mini-app/src/components/LogoutButton/index.tsx index 6ce1940..8d531e1 100644 --- a/mintro-mini-app/src/components/LogoutButton/index.tsx +++ b/mintro-mini-app/src/components/LogoutButton/index.tsx @@ -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); } From f1ed4da0a049b684e9caf12299f60929077acf79 Mon Sep 17 00:00:00 2001 From: Paolo Antonio Rossi Date: Sat, 5 Jul 2025 21:45:36 +0200 Subject: [PATCH 4/7] world: login/logout improvements --- mintro-mini-app/src/components/AuthButton/index.tsx | 3 +-- mintro-mini-app/src/hooks/useWorldcoinAuth.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mintro-mini-app/src/components/AuthButton/index.tsx b/mintro-mini-app/src/components/AuthButton/index.tsx index 7cd2e62..717ed77 100644 --- a/mintro-mini-app/src/components/AuthButton/index.tsx +++ b/mintro-mini-app/src/components/AuthButton/index.tsx @@ -79,8 +79,7 @@ export const AuthButton = () => { payload: result.finalPayload, }) ); - - // Redirect to protected home page + window.dispatchEvent(new Event("worldcoin_auth_update")); // router.push("/protected/home"); } catch (error) { console.error("Worldcoin wallet authentication error", error); diff --git a/mintro-mini-app/src/hooks/useWorldcoinAuth.ts b/mintro-mini-app/src/hooks/useWorldcoinAuth.ts index 02f5dc2..af80b08 100644 --- a/mintro-mini-app/src/hooks/useWorldcoinAuth.ts +++ b/mintro-mini-app/src/hooks/useWorldcoinAuth.ts @@ -46,6 +46,14 @@ export const useWorldcoinAuth = () => { }; loadAuthData(); + + const handleAuthUpdate = () => { + loadAuthData(); + }; + window.addEventListener("worldcoin_auth_update", handleAuthUpdate); + return () => { + window.removeEventListener("worldcoin_auth_update", handleAuthUpdate); + }; }, []); const loadUserInfo = async (address: string) => { From 854607bc0cae81373656c8149e750b2ed6a542f4 Mon Sep 17 00:00:00 2001 From: Paolo Antonio Rossi Date: Sat, 5 Jul 2025 21:49:16 +0200 Subject: [PATCH 5/7] world: add onramp funds --- mintro-mini-app/src/app/page.tsx | 45 ++++++++++++++------------------ 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/mintro-mini-app/src/app/page.tsx b/mintro-mini-app/src/app/page.tsx index 3c9e62c..35a43cb 100644 --- a/mintro-mini-app/src/app/page.tsx +++ b/mintro-mini-app/src/app/page.tsx @@ -10,28 +10,28 @@ import { UserInfo } from "@/components/UserInfo"; // import { WalletBalance } from "@/components/WalletBalance"; import { Marble, TopBar } from "@worldcoin/mini-apps-ui-kit-react"; import { AuthButton } from "@/components/AuthButton"; -// import { useCallback } from "react"; +import { useCallback } from "react"; export default function Home() { 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, - // redirectUrl: window.location.href, - // }), - // }); - // const { url } = await response.json(); - // window.open(url, "_blank"); - // } catch (e) { - // console.error(e); - // } - // }, [user]); + 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, + redirectUrl: window.location.href, + }), + }); + const { url } = await response.json(); + window.open(url, "_blank"); + } catch (e) { + console.error(e); + } + }, [user]); if (isLoading) { return ( @@ -76,18 +76,13 @@ export default function Home() { - {/* - */} + {/* */} {/* */} {/* */} From e0604dbebfe998d674d1af4ad5af5ecc100321b0 Mon Sep 17 00:00:00 2001 From: Paolo Antonio Rossi Date: Sat, 5 Jul 2025 22:28:52 +0200 Subject: [PATCH 6/7] world: improve onramp --- mintro-mini-app/package.json | 1 + mintro-mini-app/src/app/api/onramp/route.ts | 40 +++++++++- mintro-mini-app/yarn.lock | 82 ++++++++++++++++++++- package.json | 6 ++ 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 package.json diff --git a/mintro-mini-app/package.json b/mintro-mini-app/package.json index 9997112..21ed3d8 100644 --- a/mintro-mini-app/package.json +++ b/mintro-mini-app/package.json @@ -21,6 +21,7 @@ "clsx": "^2.1.1", "ethers": "^6.15.0", "iconoir-react": "^7.11.0", + "jsonwebtoken": "^9.0.2", "next": "15.2.3", "next-auth": "^5.0.0-beta.25", "react": "^19.0.0", diff --git a/mintro-mini-app/src/app/api/onramp/route.ts b/mintro-mini-app/src/app/api/onramp/route.ts index 32fb47e..89d0e5f 100644 --- a/mintro-mini-app/src/app/api/onramp/route.ts +++ b/mintro-mini-app/src/app/api/onramp/route.ts @@ -1,17 +1,51 @@ import { NextRequest, NextResponse } from "next/server"; +import jwt from "jsonwebtoken"; export async function POST(req: NextRequest) { const { address, redirectUrl } = await req.json(); + const secretBase64 = process.env.COINBASE_ONRAMP_SECRET; + + if (!secretBase64) { + return NextResponse.json( + { error: "COINBASE_ONRAMP_SECRET is not set in environment" }, + { status: 500 } + ); + } + + // Use the base64 secret directly as a buffer (this is the key fix) + const secret = Buffer.from(secretBase64, "base64"); + + const sessionToken = jwt.sign( + { + sub: address, + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 10 * 60, + }, + secret, + { algorithm: "HS256" } + ); + const baseUrl = "https://pay.coinbase.com/buy"; + const addresses = JSON.stringify({ worldchain: address }); const params = new URLSearchParams({ - appId: process.env.COINBASE_ONRAMP_APP_ID!, // Updated env var name - // New Coinbase Onramp parameters - addresses: JSON.stringify({ worldchain: address }), + appId: process.env.NEXT_PUBLIC_COINBASE_APP_ID!, + addresses, assets: JSON.stringify(["WLD"]), redirectUrl, + sessionToken, }); + console.log("params", params); + console.log("sessionToken", sessionToken); + console.log("secret", secret); + console.log("baseUrl", baseUrl); + console.log("addresses", addresses); + console.log("assets", ["WLD"]); + console.log("appId", process.env.NEXT_PUBLIC_COINBASE_APP_ID); + console.log("address", address); + console.log("redirectUrl", redirectUrl); + const onrampUrl = `${baseUrl}?${params.toString()}`; return NextResponse.json({ url: onrampUrl }); } diff --git a/mintro-mini-app/yarn.lock b/mintro-mini-app/yarn.lock index 41529e9..fb9b82a 100644 --- a/mintro-mini-app/yarn.lock +++ b/mintro-mini-app/yarn.lock @@ -2986,6 +2986,11 @@ bs58@^5.0.0: dependencies: base-x "^4.0.0" +buffer-equal-constant-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -3390,6 +3395,13 @@ duplexify@^4.1.2: readable-stream "^3.1.1" stream-shift "^1.0.2" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + elliptic@6.6.1: version "6.6.1" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" @@ -4607,6 +4619,22 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" +jsonwebtoken@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" @@ -4617,6 +4645,23 @@ json5@^1.0.2: object.assign "^4.1.4" object.values "^1.1.6" +jwa@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9" + integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== + dependencies: + buffer-equal-constant-time "^1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -4786,11 +4831,46 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lokijs@^1.5.12: version "1.5.12" resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.12.tgz#cb55b37009bdf09ee7952a6adddd555b893653a0" @@ -5691,7 +5771,7 @@ secure-password-utilities@^0.2.1: resolved "https://registry.yarnpkg.com/secure-password-utilities/-/secure-password-utilities-0.2.1.tgz#14a0d0c17c26ace573f5e5383df4cc2b51c27479" integrity sha512-znUg8ae3cpuAaogiFBhP82gD2daVkSz4Qv/L7OWjB7wWvfbCdeqqQuJkm2/IvhKQPOV0T739YPR6rb7vs0uWaw== -semver@7.7.2, semver@^7.3.8, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1: +semver@7.7.2, semver@^7.3.8, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1: version "7.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== diff --git a/package.json b/package.json new file mode 100644 index 0000000..2b60028 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "@types/jsonwebtoken": "^9.0.10", + "jsonwebtoken": "^9.0.2" + } +} From 531af77a0aa7317f5d14b6f0c721a51501911fb2 Mon Sep 17 00:00:00 2001 From: Paolo Antonio Rossi Date: Sun, 6 Jul 2025 00:50:27 +0200 Subject: [PATCH 7/7] Replace Coinbase onramp with World App Add Money Quick Action --- mintro-mini-app/.env.sample | 1 + mintro-mini-app/README.md | 18 ++++ mintro-mini-app/next.config.ts | 5 +- mintro-mini-app/package.json | 1 - .../src/app/(protected)/home/page.tsx | 2 +- mintro-mini-app/src/app/api/onramp/route.ts | 93 ++++++++++--------- mintro-mini-app/src/app/page.tsx | 2 +- 7 files changed, 75 insertions(+), 47 deletions(-) diff --git a/mintro-mini-app/.env.sample b/mintro-mini-app/.env.sample index 3ace568..a706aed 100644 --- a/mintro-mini-app/.env.sample +++ b/mintro-mini-app/.env.sample @@ -9,6 +9,7 @@ NEXT_PUBLIC_RPC_URL= NEXT_PUBLIC_COINBASE_SECRET_KEY= NEXT_PUBLIC_COINBASE_PUBLIC_KEY= NEXT_PUBLIC_COINBASE_APP_ID= +COINBASE_API_KEY_NAME= diff --git a/mintro-mini-app/README.md b/mintro-mini-app/README.md index d05d5b9..1ac5f84 100644 --- a/mintro-mini-app/README.md +++ b/mintro-mini-app/README.md @@ -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: diff --git a/mintro-mini-app/next.config.ts b/mintro-mini-app/next.config.ts index f520a86..a1311d1 100644 --- a/mintro-mini-app/next.config.ts +++ b/mintro-mini-app/next.config.ts @@ -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, }; diff --git a/mintro-mini-app/package.json b/mintro-mini-app/package.json index 21ed3d8..9997112 100644 --- a/mintro-mini-app/package.json +++ b/mintro-mini-app/package.json @@ -21,7 +21,6 @@ "clsx": "^2.1.1", "ethers": "^6.15.0", "iconoir-react": "^7.11.0", - "jsonwebtoken": "^9.0.2", "next": "15.2.3", "next-auth": "^5.0.0-beta.25", "react": "^19.0.0", diff --git a/mintro-mini-app/src/app/(protected)/home/page.tsx b/mintro-mini-app/src/app/(protected)/home/page.tsx index 8cf974d..48bf20a 100644 --- a/mintro-mini-app/src/app/(protected)/home/page.tsx +++ b/mintro-mini-app/src/app/(protected)/home/page.tsx @@ -22,7 +22,7 @@ export default function ProtectedHome() { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address: user.address, - redirectUrl: window.location.href, + amountUsd: "100", // Optional: you can make this configurable }), }); const { url } = await response.json(); diff --git a/mintro-mini-app/src/app/api/onramp/route.ts b/mintro-mini-app/src/app/api/onramp/route.ts index 89d0e5f..4ee3c5b 100644 --- a/mintro-mini-app/src/app/api/onramp/route.ts +++ b/mintro-mini-app/src/app/api/onramp/route.ts @@ -1,51 +1,58 @@ import { NextRequest, NextResponse } from "next/server"; -import jwt from "jsonwebtoken"; export async function POST(req: NextRequest) { - const { address, redirectUrl } = await req.json(); - - const secretBase64 = process.env.COINBASE_ONRAMP_SECRET; - - if (!secretBase64) { + 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: "COINBASE_ONRAMP_SECRET is not set in environment" }, + { error: "Failed to create add money URL" }, { status: 500 } ); } - - // Use the base64 secret directly as a buffer (this is the key fix) - const secret = Buffer.from(secretBase64, "base64"); - - const sessionToken = jwt.sign( - { - sub: address, - iat: Math.floor(Date.now() / 1000), - exp: Math.floor(Date.now() / 1000) + 10 * 60, - }, - secret, - { algorithm: "HS256" } - ); - - const baseUrl = "https://pay.coinbase.com/buy"; - const addresses = JSON.stringify({ worldchain: address }); - const params = new URLSearchParams({ - appId: process.env.NEXT_PUBLIC_COINBASE_APP_ID!, - addresses, - assets: JSON.stringify(["WLD"]), - redirectUrl, - sessionToken, - }); - - console.log("params", params); - console.log("sessionToken", sessionToken); - console.log("secret", secret); - console.log("baseUrl", baseUrl); - console.log("addresses", addresses); - console.log("assets", ["WLD"]); - console.log("appId", process.env.NEXT_PUBLIC_COINBASE_APP_ID); - console.log("address", address); - console.log("redirectUrl", redirectUrl); - - const onrampUrl = `${baseUrl}?${params.toString()}`; - return NextResponse.json({ url: onrampUrl }); } diff --git a/mintro-mini-app/src/app/page.tsx b/mintro-mini-app/src/app/page.tsx index 35a43cb..9faca80 100644 --- a/mintro-mini-app/src/app/page.tsx +++ b/mintro-mini-app/src/app/page.tsx @@ -23,7 +23,7 @@ export default function Home() { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ address: user.address, - redirectUrl: window.location.href, + amountUsd: "100", // Optional: you can make this configurable }), }); const { url } = await response.json();