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>
);
}
| Status | Meaning | Show |
|---|---|---|
connecting | Finding out whether the app is inside CrackPay | A loading state, never a connect button |
connected | Inside CrackPay; account, handle and provider are set | Your app |
unavailable | Opened outside CrackPay | "Open this app from CrackPay" |
error | Something failed while connecting | A 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
npm run dev. In a normal tab you should see "Open this app from CrackPay".- Expose it with
ngrok http 5173. - Load the HTTPS URL in CrackPay's Developer mode. You should see your handle.
See Test in CrackPay.
Next steps
- Wallet connection, including wagmi
- Interacting with smart contracts
- Best practices