> ## Documentation Index
> Fetch the complete documentation index at: https://docs.legend.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Using Privy Wallets

> Connect Privy-managed embedded wallets to Legend for DeFi yield

# Using Privy Wallets

If you use [Privy](https://www.privy.io/) to manage wallets for your users, you can connect those wallets to Legend to access DeFi yield, swaps, and transfers — without changing your key management setup.

This guide covers the integration pattern: how a Privy embedded wallet becomes a Legend sub-account signer, and how to sign Legend plans using Privy's `secp256k1_sign` method.

## How it fits together

Privy handles wallet creation and key management. Legend handles DeFi operations. The two connect through the wallet's EOA address.

```mermaid theme={null}
sequenceDiagram
    participant U as User (Browser)
    participant P as Privy
    participant S as Your Server
    participant L as Legend

    U->>P: Authenticate
    P-->>U: Embedded wallet (EOA)
    U->>S: Wallet address
    S->>L: Create sub-account (signer_address)
    L-->>S: account_id + legend_wallet_address

    Note over S,L: Later, when earning yield...

    S->>L: POST /plan/earn
    L-->>S: plan with EIP-712 digest
    S-->>U: digest to sign
    U->>P: secp256k1_sign(digest)
    P-->>U: signature
    U->>S: signature
    S->>L: POST /plan/execute { signature }
    L-->>S: { status: executing }
```

Legend never sees the private key. Privy holds it. Your app passes the plan's EIP-712 digest to Privy for signing, then relays the signature back to Legend for execution.

## Prerequisites

* A Legend Prime Account with a query key ([contact us](https://legend.xyz) to get set up)
* A Privy app with embedded wallets enabled
* `legend-prime` and `@privy-io/react-auth` installed

```bash theme={null}
npm install legend-prime @privy-io/react-auth
```

## Step 1 — Get the Privy wallet address

When a user authenticates with Privy, they get an embedded wallet. Use its address as the Legend signer.

```typescript theme={null}
import { useWallets } from "@privy-io/react-auth";

const { wallets } = useWallets();
const embeddedWallet = wallets.find((w) => w.walletClientType === "privy");
const signerAddress = embeddedWallet.address;
```

This is a standard Ethereum EOA address. Legend doesn't need to know that Privy manages the key behind it.

## Step 2 — Create a Legend sub-account

Pass the wallet address to Legend as an `eoa` signer. This is typically done from your backend.

```typescript theme={null}
import { LegendPrime } from "legend-prime";

const client = new LegendPrime({
  queryKey: process.env.LEGEND_QUERY_KEY,
});

const account = await client.accounts.create({
  signerType: "eoa",
  signerAddress: signerAddress, // The Privy wallet address
});

// Save these — you'll need them
const accountId = account.account_id;
const walletAddress = account.legend_wallet_address;
```

Legend creates an on-chain smart wallet (Quark Wallet) for the account. The `legend_wallet_address` is where funds live.

<Warning>
  **Fund the Legend wallet, not the Privy EOA.** Send assets to `legend_wallet_address` on a supported network (Base, Ethereum Mainnet, Arbitrum, or Optimism). The Privy wallet address is the signer, not the asset holder.
</Warning>

## Step 3 — Create a plan

When your user wants to earn yield (or swap, transfer, etc.), create a plan through Legend. The plan response includes an EIP-712 digest — a hash that needs to be signed by the wallet's key.

```typescript theme={null}
const plan = await client.plan.earn(accountId, {
  amount: "1000000", // 1 USDC (6 decimals)
  asset: "USDC",
  network: "base",
  protocol: "compound",
});

const digest = plan.details.eip712_data.digest; // "0x8a3f...b7c2"
```

<Note>
  Plans expire after 2 minutes. Sign and execute before `expires_at` or create a new plan.
</Note>

## Step 4 — Sign the digest with Privy

This is where the two systems meet. Pass the EIP-712 digest to Privy's `secp256k1_sign` method, which performs raw curve-level signing over the hash — exactly what Legend expects.

```typescript theme={null}
const provider = await embeddedWallet.getEthereumProvider();

const signature = await provider.request({
  method: "secp256k1_sign",
  params: [digest],
});
```

`secp256k1_sign` signs the hash directly without any prefix or transformation. This is the same operation that viem's `signer.sign({ hash })` performs — just routed through Privy's key management instead of a local private key.

<Note>
  Don't use `personal_sign` or `eth_signTypedData_v4` here. `personal_sign` adds an Ethereum message prefix that produces the wrong signature. `eth_signTypedData_v4` requires the full EIP-712 typed data object and recomputes the digest internally — unnecessary since Legend already provides the digest.
</Note>

## Step 5 — Execute the plan

Send the signature back to Legend. This triggers the on-chain transaction.

```typescript theme={null}
const result = await client.plan.execute(accountId, {
  planId: plan.plan_id,
  signature,
});

console.log(result.status); // "executing"
```

## Step 6 — Monitor

Poll activities or use long-polling events to confirm the transaction.

```typescript theme={null}
const { activities } = await client.accounts.activities(accountId);
console.log(activities[0].activity_status); // "pending" → "confirmed"
```

## Architecture patterns

Privy + Legend integrations typically follow one of two patterns:

### User-facing app

Your frontend handles Privy auth and signing. Your backend handles Legend API calls. Users see an "Earn Yield" button, click it, approve the signing request, and funds are deployed.

| Layer        | Responsibility                              |
| ------------ | ------------------------------------------- |
| **Frontend** | Privy auth, wallet access, `secp256k1_sign` |
| **Backend**  | Legend SDK, plan creation, plan execution   |

### Automated backend

Your backend manages Privy wallets via the [server SDK](https://docs.privy.io) and signs on behalf of users (with their consent). No user interaction needed for signing.

```typescript theme={null}
import Privy from "@privy-io/node";

const privy = new Privy({ appId: "...", appSecret: "..." });

// Sign the digest server-side
const { signature } = await privy
  .wallets()
  .ethereum()
  .secp256k1Sign(walletId, {
    params: { hash: digest },
  });
```

This pattern works well for omnibus accounts or automated strategies where your server controls the signing flow.

## Full example

Here's a complete React component that creates an earn plan and signs it with Privy:

```typescript theme={null}
import { useWallets } from "@privy-io/react-auth";
import { LegendPrime } from "legend-prime";

const client = new LegendPrime({
  queryKey: process.env.NEXT_PUBLIC_LEGEND_QUERY_KEY,
});

function EarnButton({ accountId }: { accountId: string }) {
  const { wallets } = useWallets();

  async function handleEarn() {
    const wallet = wallets.find((w) => w.walletClientType === "privy");
    const provider = await wallet.getEthereumProvider();

    // 1. Create the plan
    const plan = await client.plan.earn(accountId, {
      amount: "1000000",
      asset: "USDC",
      network: "base",
      protocol: "compound",
    });

    // 2. Sign the EIP-712 digest with Privy
    const signature = await provider.request({
      method: "secp256k1_sign",
      params: [plan.details.eip712_data.digest],
    });

    // 3. Execute
    await client.plan.execute(accountId, {
      planId: plan.plan_id,
      signature,
    });
  }

  return <button onClick={handleEarn}>Earn 1 USDC on Compound</button>;
}
```

## Next steps

* [Plan-Sign-Execute](/concepts/plan-execute-flow) — Understand the signing model in depth
* [Earn Yield](/guides/earn) — More detail on earn plans and supported protocols
* [Monitoring](/guides/monitoring) — Track transaction status with activities and events
* [TypeScript SDK](/sdks/typescript) — Full SDK reference
