# Stablecoin mobile app

Build a React Native Expo application that provisions embedded wallets, funds them from a faucet, and performs gas-sponsored USDC transfers between two wallets on Ethereum Sepolia.

:::tip
You'll build a mobile app where users create two embedded wallets, fund one with testnet USDC, and transfer USDC between them — all without paying gas fees.
:::

<VideoSnippet title="USDC Transfer Demo" src="KA8RzSKX5c0" variant="full" />

<HoverCardLink
  title="View Sample Code"
  subtitle="GitHub Repository"
  description="Complete source code for the USDC Transfer Demo with setup instructions and implementation details."
  href="https://github.com/openfort-xyz/recipes-hub/tree/main/usdc"
  img={{
  src: "/img/icons/github-icon.svg",
  alt: "GitHub Icon",
  className: "rounded-none",
}}
  color="#333"
  external
/>

## Getting started

::::steps

### Set up your project

Clone the recipe and install dependencies:

```bash
pnpx gitpick openfort-xyz/recipes-hub/tree/main/usdc openfort-usdc
cd openfort-usdc
pnpm install
```

### Set up the backend

The app requires an external backend server for Openfort Shield authentication. This backend creates encrypted sessions that the SDK uses for non-custodial wallet recovery.

Clone and start the backend quickstart:

```bash
git clone https://github.com/openfort-xyz/openfort-backend-quickstart.git
cd openfort-backend-quickstart
cp .env.example .env
```

Add your Openfort **Secret Key** (`sk_...`) to the backend `.env`, then start the server:

```bash
pnpm install
pnpm dev
```

The backend runs on `http://localhost:3000` and exposes the `/api/protected-create-encryption-session` endpoint used by the mobile app during wallet creation and recovery.

### Configure your Openfort credentials

Return to the `openfort-usdc` directory and create your environment file:

```bash
cp .env.example .env.local
```

Fill in your credentials from the [Openfort Dashboard](https://dashboard.openfort.io):

```bash
OPENFORT_PUBLISHABLE_KEY=pk_...
OPENFORT_SHIELD_PUBLISHABLE_KEY=...
OPENFORT_SHIELD_RECOVERY_BASE_URL=http://localhost:3000
OPENFORT_FEE_SPONSORSHIP_ID=pol_...
```

:::tip
Find your API keys under **Developers** → **API Keys**. Create a gas sponsorship under **gas sponsorships** to get the policy ID — this is what enables gasless transfers for your users.
:::

### Configure the wallet provider

The app wraps everything in an `OpenfortProvider` that configures the embedded wallet, supported chains, and gas sponsorship. This is the root of the wallet infrastructure:

```tsx
// app/_layout.tsx
<OpenfortProvider
  publishableKey={publishableKey}
  walletConfig={{
    feeSponsorshipId: ethereumProviderPolicyId,
    shieldPublishableKey,
    getEncryptionSession: getEncryptionSessionFromEndpoint,
  }}
  supportedChains={[
    {
      id: 84532,
      name: 'Base Sepolia',
      nativeCurrency: { name: 'Base Sepolia Ether', symbol: 'ETH', decimals: 18 },
      rpcUrls: { default: { http: ['https://sepolia.base.org'] } },
    },
    {
      id: 11155111,
      name: 'Sepolia',
      nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
      rpcUrls: { default: { http: ['https://ethereum-sepolia-rpc.publicnode.com'] } },
    },
  ]}
>
```

The `feeSponsorshipId` enables the Openfort paymaster to sponsor gas so users never need testnet ETH.

### Understand the transfer flow

The core of this recipe is the USDC transfer function, which uses EIP-5792 `wallet_sendCalls` to send gas-sponsored ERC-20 transfers through the Openfort smart account:

```typescript
// utils/erc20.ts
const provider = await fromWallet.wallet.getProvider();

// Encode the ERC-20 transfer calldata
const amountUnits = parseAmountToUnits(amount, USDC_DECIMALS);
const amountHex = toHex32(amountUnits);
const transferData = buildTransferData(toAddress, amountHex);

// Send via EIP-5792 wallet_sendCalls (gas-sponsored by the paymaster)
const transactionIntentId = await provider.request({
  method: "wallet_sendCalls",
  params: [{
    version: "1.0",
    chainId: "0xaa36a7", // Ethereum Sepolia
    from: fromWallet.address,
    calls: [
      { to: USDC_CONTRACT_ADDRESS, value: "0x0", data: transferData },
    ],
  }],
});
```

The function uses `wallet_sendCalls` instead of `eth_sendTransaction` because Openfort wallets are smart accounts that support batch calls and paymaster sponsorship through EIP-5792.

### Run the application

```bash
pnpm run ios     # Launch on iOS simulator
pnpm run android # Launch on Android emulator
```

The app guides you through:

1. Authenticating as a guest and creating two embedded wallets
2. Funding Wallet B with Sepolia USDC from [Circle's faucet](https://faucet.circle.com/)
3. Transferring USDC between wallets with gas sponsorship

::::

## How it works

The app follows a 4-screen state machine: **Create Wallets** → **Faucet** → **Waiting for Funds** → **Main App**.

**Wallet creation** uses the `useEmbeddedEthereumWallet` hook from `@openfort/react-native`. Each call to `createWallet` provisions a new embedded wallet on Ethereum Sepolia:

```tsx
// components/onboarding/CreateWalletsScreen.tsx
createWallet({
  chainId: 11155111,
  onSuccess: ({ wallet }) => {
    const walletData = {
      address: wallet.address,
      balance: "0",
      wallet: wallet
    };
    onWalletACreated(walletData);
  },
});
```

**Balance polling** uses a custom `useUsdcBalance` hook that reads the USDC contract's `balanceOf` via `eth_call` and polls at configurable intervals:

```typescript
// components/onboarding/WaitingForFundsScreen.tsx
const { balance: currentBalance, hasBalance } = useUsdcBalance({
  activeWalletOrProvider: activeWallet,
  ownerAddress: walletB?.address,
  onBalanceUpdate: onUpdateBalance,
  options: { pollIntervalMs: 5000, stopWhenPositive: true, timeoutMs: ERC20_BALANCE_TIMEOUT_MS }
});
```

**Wallet switching** lets users toggle the active sender between Wallet A and Wallet B using `setActiveWallet` from the Openfort SDK, making it easy to demonstrate bidirectional transfers.

| Component | Technology |
|-----------|------------|
| **Frontend** | React Native + Expo |
| **Blockchain** | Ethereum Sepolia & Base Sepolia |
| **Key Libraries** | `@openfort/react-native`, `expo-router` |
| **Tooling** | Expo CLI |

## Next steps

* [Yield on Aave](/docs/recipes/aave) — Use embedded wallets for DeFi lending on Aave
* [Yield on Morpho](/docs/recipes/morpho) — Supply USDC to Morpho Blue vaults on Base
* [Server-side wallets](/docs/products/server/setup) — Set up backend wallet infrastructure
