Skip to main content

Setting up a React app

Build a Mini App with Vite, React and TypeScript, connected to CrackPay.

:::tip Faster with AI The build-crackpay-miniapp skill generates this whole project for you, ready to run. :::

Create your project​

npm create vite@latest my-miniapp -- --template react-ts
cd my-miniapp
npm install

Install the SDK​

npm install @crackpay/miniapp-sdk viem

Connect on load​

useCrackPay() connects when your app mounts and reports where it got to. There is nothing for the user to click.

src/App.tsx
import { useCrackPay } from "@crackpay/miniapp-sdk/react";

export function App() {
const crackpay = useCrackPay();

if (crackpay.status === "connecting") return <p>Loading…</p>;
if (crackpay.status === "unavailable") return <p>Open this app from CrackPay.</p>;
if (crackpay.status === "error") return <p>Something went wrong. Close the app and open it again.</p>;

return (
<main>
<h1>{crackpay.handle ? `Hi @${crackpay.handle}` : "Welcome"}</h1>
<p>Account: {crackpay.account}</p>
</main>
);
}
StatusMeaningShow
connectingFinding out whether the app is inside CrackPayA loading state, never a connect button
connectedInside CrackPay; account, handle and provider are setYour app
unavailableOpened outside CrackPay"Open this app from CrackPay"
errorSomething failed while connectingA short message; log crackpay.error

Call useCrackPay() once near the root and pass account and provider down.

Read the balance​

src/crackpay.ts
import { getArcChain, getTokens, type MiniAppProvider } from "@crackpay/miniapp-sdk";
import { createPublicClient, erc20Abi, http, type Address } from "viem";
import { arc, arcTestnet } from "viem/chains";

/** The Arc network this CrackPay runs on: mainnet or testnet. */
export async function crackPayChain(provider: MiniAppProvider) {
const info = getArcChain((await provider.request({ method: "eth_chainId" })) as string);
if (!info) throw new Error("Unknown network");
return { info, chain: info.testnet ? arcTestnet : arc, tokens: getTokens(info.id)! };
}

/** USDC balance in 6-decimal units. */
export async function readUsdcBalance(provider: MiniAppProvider, account: Address) {
const { info, chain, tokens } = await crackPayChain(provider);
const publicClient = createPublicClient({ chain, transport: http(info.rpcUrl) });
return publicClient.readContract({
address: tokens.USDC.address,
abi: erc20Abi,
functionName: "balanceOf",
args: [account],
});
}

Format it with formatUnits(balance, 6). Prefer connectCrackPay() from @crackpay/miniapp-sdk/viem, which does this lookup for you and returns chain.

Send a payment​

src/crackpay.ts
import { createWalletClient, custom, type EIP1193Provider } from "viem";

export async function sendUsdc(provider: MiniAppProvider, account: Address, to: Address, value: bigint) {
const { chain } = await crackPayChain(provider);
const walletClient = createWalletClient({
chain,
transport: custom(provider as unknown as EIP1193Provider),
});
// CrackPay shows the user a confirmation and handles gas.
return walletClient.sendTransaction({ account, chain, to, value });
}

value is native USDC with 18 decimals: parseEther("1.50"). More in Send a transaction.

Verify the connection​

  1. npm run dev. In a normal tab you should see "Open this app from CrackPay".
  2. Expose it with ngrok http 5173.
  3. Load the HTTPS URL in CrackPay's Developer mode. You should see your handle.

See Test in CrackPay.

Next steps​