> 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/turnkey-integration.md).

# Turnkey Integration (coming soon)

[Turnkey](https://turnkey.com) provisions embedded wallets from an email-OTP, passkey, or OAuth login via its **Auth Proxy** (no backend required). A Turnkey embedded wallet is an ordinary EOA, so once you can sign with it, the on-chain path is the standard Direct EOA flow — `approve` / `createPosition` / `requestClose` over `viem`.

The one difference from [Privy](/for-developers/wallet-integration/privy-integration.md): **Turnkey ships no wagmi connector**, so if your app is built on wagmi you bridge Turnkey's signer in with a small custom connector. That bridge is the only Turnkey-specific code; nothing in the contract layer changes.

For the core lifecycle, ABIs, and the Direct EOA snippets, see [On-Chain Integration](/for-developers/wallet-integration/on-chain-integration.md).

***

## 1. Install and wrap your app

```bash
npm install @turnkey/react-wallet-kit @turnkey/viem
```

```tsx
import {TurnkeyProvider} from "@turnkey/react-wallet-kit";
import "@turnkey/react-wallet-kit/styles.css";

export function Providers({children}: {children: React.ReactNode}) {
  return (
    <TurnkeyProvider
      config={{
        organizationId: import.meta.env.VITE_TURNKEY_ORG_ID,
        authProxyConfigId: import.meta.env.VITE_TURNKEY_AUTH_PROXY_CONFIG_ID,
      }}
    >
      {children}
    </TurnkeyProvider>
  );
}
```

Both ids come from the [Turnkey dashboard](https://dashboard.turnkey.com): your **organization ID** and the **WalletKit / Auth Proxy config ID** (enable Auth Proxy and pick your auth methods first). Both are public client identifiers.

***

## 2. Log in and derive a viem account

`useTurnkey()` exposes the login trigger, the authenticated client, and the user's wallets. Each wallet account carries its own (sub-)`organizationId` — that's what `createAccount` must sign under.

```tsx
import {useTurnkey, AuthState} from "@turnkey/react-wallet-kit";
import {createAccount} from "@turnkey/viem";

function useTurnkeyAccount() {
  const {authState, httpClient, wallets} = useTurnkey();

  async function buildAccount() {
    if (authState !== AuthState.Authenticated || !httpClient) return null;
    const eth = wallets
      .flatMap((w) => w.accounts ?? [])
      .find((a) => a.addressFormat === "ADDRESS_FORMAT_ETHEREUM");
    if (!eth) return null;

    // A viem LocalAccount that signs through Turnkey.
    return createAccount({
      client: httpClient,
      organizationId: eth.organizationId,
      signWith: eth.address,
    });
  }

  return buildAccount;
}
```

`handleLogin()` from `useTurnkey()` opens the login modal.

***

## 3a. Open and close — plain viem (no wagmi)

If you are **not** on wagmi, use the Turnkey account directly with a viem walletClient. This is the simplest path.

```typescript
import {createWalletClient, http} from "viem";
import {polygon} from "viem/chains";
import {
  buildApproveTx,
  buildCreatePositionTx,
  buildRequestCloseTx,
  verifyQuoteSignature,
} from "@dimes-dot-fi/sdk/contract";

const account = await buildAccount();           // from step 2
const walletClient = createWalletClient({account, chain: polygon, transport: http()});

// Pass account.address as `wallet_address` when requesting the quote.

await walletClient.writeContract(
  buildApproveTx(usdcAddress, offer.polygonVaultContractAddress, approvalAmount),
);
await verifyQuoteSignature(client, offer, account.address);
await walletClient.writeContract(buildCreatePositionTx(offer));

// later
await walletClient.writeContract(
  buildRequestCloseTx(vaultAddress, position.on_chain_position_key),
);
```

***

## 3b. Open and close — bridging into wagmi

If your app already uses wagmi (so `useWriteContract`, `useAccount`, etc. just work), wrap the Turnkey account in an **EIP-1193 provider** and expose it through a custom wagmi connector. Delegate signing/sending to a viem walletClient and let reads fall through to the RPC:

```typescript
import {
  createPublicClient, createWalletClient, http,
  hexToBigInt, hexToNumber, numberToHex,
  type Chain, type EIP1193Provider, type LocalAccount,
} from "viem";

export function createTurnkeyProvider(account: LocalAccount, chain: Chain): EIP1193Provider {
  const transport = http();
  const publicClient = createPublicClient({chain, transport});
  const walletClient = createWalletClient({account, chain, transport});

  const request = async ({method, params = []}: {method: string; params?: any[]}) => {
    switch (method) {
      case "eth_requestAccounts":
      case "eth_accounts":
        return [account.address];
      case "eth_chainId":
        return numberToHex(chain.id);
      case "personal_sign":
        return walletClient.signMessage({message: {raw: params[0]}});
      case "eth_signTypedData_v4":
        return account.signTypedData(
          typeof params[1] === "string" ? JSON.parse(params[1]) : params[1],
        );
      case "eth_sendTransaction": {
        const tx = params[0];
        return walletClient.sendTransaction({
          account, chain,
          to: tx.to, data: tx.data,
          value: tx.value ? hexToBigInt(tx.value) : undefined,
          gas: tx.gas ? hexToBigInt(tx.gas) : undefined,
          nonce: tx.nonce != null ? hexToNumber(tx.nonce) : undefined,
        });
      }
      default:
        return publicClient.request({method, params} as any);
    }
  };
  return {request, on: () => {}, removeListener: () => {}} as unknown as EIP1193Provider;
}
```

Wrap it in a wagmi connector with `createConnector`, reading the live account from a module-level holder you populate on login (the signer only exists after the user authenticates). Then keep wagmi in sync with the Turnkey session: on login, build the account, set the holder, and call wagmi `connect`; on logout, clear it and `disconnect`. With that in place, the existing `createPosition` / `requestClose` wagmi calls run unchanged.

> The official demo UI implements exactly this — see [`dimes-demo-ui`](https://github.com/dimes-fi/dimes-demo-ui) (`src/turnkey/provider.ts`, `src/turnkey/connector.ts`, and the `TurnkeyBridge` in `src/WalletProviders.tsx`).

***

## Funding the embedded wallet

`handleOnRamp()` from `useTurnkey()` opens the funding flow; or send pUSD and a little POL to the wallet directly. The vault pulls `total_user_amount_usdc_units` of pUSD; the wallet needs POL for gas.

***

## Notes

* **`wallet_address`.** Pass the Turnkey wallet's Ethereum address — the one that signs on-chain — as `wallet_address` to `POST /prediction-markets/quotes`.
* **Sub-organizations.** Each end user gets a Turnkey sub-org; sign under the account's own `organizationId`, not the parent org id.
* **Export.** `handleExportWallet({walletId})` lets a user self-custody.
