> For the complete documentation index, see [llms.txt](https://docs.dimes.fi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.dimes.fi/for-developers/wallet-integration/privy-integration.md).

# Privy Integration

A Privy embedded wallet is an EOA and ships with a **wagmi connector**, so it drops into the [Direct EOA flow](/for-developers/wallet-integration/on-chain-integration.md) unchanged — the vault sees an ordinary `msg.sender`. Enabling Privy **smart wallets** instead makes `msg.sender` an ERC-4337 account; see [Smart wallets](#smart-wallets-account-abstraction).

Both flows are verified end-to-end in the [demo UI](https://github.com/dimes-fi/dimes-demo-ui) (`WalletProviders.tsx`, `config.privy.ts`, `ConnectControls.tsx`).

***

## 1. Wrap your app

Use Privy's `WagmiProvider` (from `@privy-io/wagmi`), not wagmi's own — it wires the connector and auto-connect.

```bash
npm install @privy-io/react-auth @privy-io/wagmi
```

```tsx
import {PrivyProvider} from "@privy-io/react-auth";
import {WagmiProvider, createConfig} from "@privy-io/wagmi";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
import {http} from "wagmi";
import {polygon} from "wagmi/chains";

// No `connectors` — PrivyProvider injects the wallet connector.
const wagmiConfig = createConfig({
  chains: [polygon],
  transports: {[polygon.id]: http()},
});

const queryClient = new QueryClient();

export function Providers({children}: {children: React.ReactNode}) {
  return (
    <PrivyProvider
      appId={import.meta.env.VITE_PRIVY_APP_ID}
      config={{
        defaultChain: polygon,
        supportedChains: [polygon],
        embeddedWallets: {ethereum: {createOnLogin: "users-without-wallets"}},
      }}
    >
      <QueryClientProvider client={queryClient}>
        <WagmiProvider config={wagmiConfig}>{children}</WagmiProvider>
      </QueryClientProvider>
    </PrivyProvider>
  );
}
```

`appId` is a public client identifier. Get it from the [Privy dashboard](https://dashboard.privy.io).

***

## 2. Read the wallet address

The address that signs on-chain is the `wallet_address` you must quote for. After login, read it from wagmi:

```tsx
const {ready, authenticated, login} = usePrivy();
const {address} = useAccount();
```

***

## 3. Open and close

Each call is a wagmi `writeContract`, signed by Privy — identical to the [Direct EOA flow](/for-developers/wallet-integration/on-chain-integration.md#opening-a-position-direct-eoa).

{% tabs %}
{% tab title="SDK (viem)" %}

```typescript
import {useWalletClient} from "wagmi";
import {
  buildApproveTx,
  buildCreatePositionTx,
  buildRequestCloseTx,
  verifyQuoteSignature,
} from "@dimes-dot-fi/sdk/contract";

const {data: walletClient} = useWalletClient();

// Approve pUSD, verify the quote signature, open.
await walletClient.writeContract(
  buildApproveTx(pUsdAddress, offer.polygonVaultContractAddress, approvalAmount),
);
await verifyQuoteSignature(client, offer, address);
await walletClient.writeContract(buildCreatePositionTx(offer));

// Close later — only the owner (this wallet) can call requestClose.
await walletClient.writeContract(
  buildRequestCloseTx(vaultAddress, position.on_chain_position_key),
);
```

{% endtab %}

{% tab title="wagmi hooks" %}

```typescript
import {useWriteContract} from "wagmi";

const {writeContractAsync} = useWriteContract();

// Args come straight from the quote response.
await writeContractAsync({
  address: offer.polygonVaultContractAddress,
  abi: VAULT_ABI,
  functionName: "createPosition",
  args: [
    offer.positionSeedHex,
    offer.polymarketMarketId,
    BigInt(offer.polymarketTokenId),
    BigInt(offer.collateralUsdcUnits),
    offer.leverageBps,
    BigInt(offer.notionalUsdcUnits),
    offer.originationFeeBps,
    offer.lifetimeFeeAprBps,
    offer.liquidationFeeBps,
    BigInt(offer.expectedOpenTradingFeeUsdcUnits),
    offer.contractSignature,
    BigInt(offer.signatureExpiry),
  ],
});
```

{% endtab %}
{% endtabs %}

The backend handles the rest — exchange order on open, proceeds on close, with `position.opened` / `position.closed` over [WebSocket](/for-developers/api-and-events/websocket.md).

***

## Smart wallets (Account Abstraction)

Enable *Smart wallets* in the dashboard and each user also gets an ERC-4337 account that becomes `msg.sender`. Two changes from the EOA flow:

1. **Quote for the smart account.** `wallet_address` is `client.account.address` (the contract that signs), not the owner EOA.
2. **Submit via the smart-wallet client**, batching approve + open into one userOperation.

```typescript
import {useSmartWallets} from "@privy-io/react-auth/smart-wallets";
import {encodeFunctionData} from "viem";
import {buildApproveTx, buildCreatePositionTx} from "@dimes-dot-fi/sdk/contract";

const {client} = useSmartWallets();           // undefined until a smart wallet exists

const approve = buildApproveTx(pUsdAddress, offer.polygonVaultContractAddress, approvalAmount);
const create = buildCreatePositionTx(offer);

const hash = await client.sendTransaction({
  account: client.account,                    // smart account = msg.sender
  calls: [
    {to: approve.address, data: encodeFunctionData(approve), value: 0n},
    {to: create.address,  data: encodeFunctionData(create),  value: 0n},
  ],
});
```

Closing is the same with a single `requestClose` call.

A 4337 wallet needs a **bundler** (Privy dashboard). Use a keyed endpoint — `https://api.pimlico.io/v2/137/rpc?apikey=<key>`; the keyless `public.pimlico.io` blocks browser CORS. Add a **paymaster** for gasless opens; without one the smart account pays its own gas (fund it with POL — the first userOp also deploys it).

Verified end-to-end in the demo UI (`contract/smartWalletHooks.ts`, `SmartWalletBridge` in `WalletProviders.tsx`).

***

## Notes

* **Funding.** `useFundWallet` opens Privy's on-ramp/transfer flow; or send pUSD and a little POL to the wallet directly.
* **Export.** `useExportWallet` reveals the embedded key for self-custody.
* **One switch.** In the demo UI the whole RainbowKit ↔ Privy swap is gated on `VITE_PRIVY_APP_ID`; the contract layer is untouched.
