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

# Smart Wallets

> Deposit integration examples for Privy Smart Wallets and Alchemy Smart Accounts

Smart wallets enable transaction batching and gas sponsorship, providing a better user experience than traditional EOA wallets. This guide covers integration patterns for both Privy Smart Wallets and Alchemy Smart Accounts (ERC-4337).

<Info>
  **Key difference from EOA wallets**: Smart wallets cannot sign EIP-712 permit
  messages, so they must use the approval flow. However, they can batch the
  approval and deposit into a single user confirmation.
</Info>

## Wallet Type Comparison

| Aspect              | EOA (Privy)                    | Privy Smart Wallet             | Alchemy Smart Account            |
| ------------------- | ------------------------------ | ------------------------------ | -------------------------------- |
| **Send Method**     | `sendTransaction()`            | `sendTransaction({ calls })`   | `sendTransactions({ requests })` |
| **Batching**        | No                             | Yes                            | Yes                              |
| **Permit Support**  | Yes                            | No                             | No                               |
| **Gas Payment**     | User                           | Sponsored                      | Sponsored                        |
| **Return Value**    | `txHash`                       | `txHash`                       | `userOpHash`                     |
| **Receipt Polling** | `useWaitForTransactionReceipt` | `useWaitForTransactionReceipt` | `waitForUserOperationReceipt`    |
| **USDT Deposit**    | 2 transactions                 | 1 batched tx                   | 1 UserOperation                  |
| **USDC Deposit**    | 1 tx (permit)                  | 1 batched tx                   | 1 UserOperation                  |

## Privy Smart Wallet

Uses Privy's embedded smart contract wallet with gas sponsorship and transaction batching.

### Imports

```ts theme={null}
// SDK imports
import {
  prepareDepositAuthorization,
  prepareDeposit,
  isApprovalAuth,
  isAlreadyApprovedAuth,
  YieldType,
} from '@paxoslabs/amplify-sdk'

// Privy imports
import { useSmartWallets } from '@privy-io/react-auth/smart-wallets'

// Viem imports
import { encodeFunctionData, formatUnits } from 'viem'
```

### usePrivySmartWalletDeposit Hook

```tsx theme={null}
// src/hooks/usePrivySmartWalletDeposit.ts
import { useState, useCallback } from 'react'
import { useSmartWallets } from '@privy-io/react-auth/smart-wallets'
import { encodeFunctionData, formatUnits } from 'viem'
import {
  prepareDepositAuthorization,
  prepareDeposit,
  isApprovalAuth,
  isAlreadyApprovedAuth,
  YieldType,
} from '@paxoslabs/amplify-sdk'

interface DepositParams {
  depositAsset: `0x${string}`
  amount: bigint
  signerAddress: `0x${string}`
  vaultName: string
}

export function usePrivySmartWalletDeposit() {
  const { client: smartWalletClient } = useSmartWallets()
  const [status, setStatus] = useState<string>('')
  const [isLoading, setIsLoading] = useState(false)

  const deposit = useCallback(
    async ({
      depositAsset,
      amount,
      signerAddress,
      vaultName,
    }: DepositParams) => {
      if (!smartWalletClient) {
        throw new Error('Smart wallet not connected')
      }

      setIsLoading(true)

      try {
        const params = {
          vaultName,
          depositAsset,
          depositAmount: formatUnits(amount, 6),
          to: signerAddress,
          chainId: 1,
          forceMethod: 'approval' as const, // Smart wallets can't sign permits
        }

        // Step 1: Get authorization (forces approval method)
        setStatus('Preparing transaction...')
        const auth = await prepareDepositAuthorization(params)

        // Step 2: Prepare deposit tx
        const prepared = await prepareDeposit(params)

        // Step 3: Batch approve + deposit in ONE transaction
        if (isApprovalAuth(auth)) {
          const calls = [
            {
              to: auth.txData.address, // Token contract
              data: encodeFunctionData({
                abi: auth.txData.abi,
                functionName: auth.txData.functionName,
                args: auth.txData.args,
              }),
            },
            {
              to: prepared.txData.address, // Depositor contract
              data: encodeFunctionData({
                abi: prepared.txData.abi,
                functionName: prepared.txData.functionName,
                args: prepared.txData.args,
              }),
            },
          ]

          setStatus('Please confirm in your wallet...')

          // Single batched transaction with gas sponsorship
          const hash = await smartWalletClient.sendTransaction(
            { calls },
            {
              sponsor: true, // Gas is sponsored
              uiOptions: {
                description: `Depositing ${formatUnits(amount, 6)} tokens`,
              },
            }
          )

          setStatus('Deposit successful!')
          return hash
        } else if (isAlreadyApprovedAuth(auth)) {
          // No approval needed, just deposit
          setStatus('Please confirm in your wallet...')

          const hash = await smartWalletClient.sendTransaction(
            {
              calls: [
                {
                  to: prepared.txData.address,
                  data: encodeFunctionData({
                    abi: prepared.txData.abi,
                    functionName: prepared.txData.functionName,
                    args: prepared.txData.args,
                  }),
                },
              ],
            },
            {
              sponsor: true,
              uiOptions: {
                description: `Depositing ${formatUnits(amount, 6)} tokens`,
              },
            }
          )

          setStatus('Deposit successful!')
          return hash
        }
      } catch (error) {
        setStatus(
          `Error: ${error instanceof Error ? error.message : 'Unknown'}`
        )
        throw error
      } finally {
        setIsLoading(false)
      }
    },
    [smartWalletClient]
  )

  return { deposit, status, isLoading }
}
```

### Key Characteristics

* Uses `smartWalletClient.sendTransaction({ calls })` for batching multiple contract calls
* `sponsor: true` enables gas sponsorship (users don't pay gas)
* Cannot use permit signatures (smart contracts can't sign EIP-712)
* Batches approve + deposit into a single user confirmation
* Returns a standard transaction hash

***

## Alchemy Smart Account (ERC-4337)

Uses ERC-4337 Account Abstraction via Alchemy's SDK with Dynamic as the auth provider.

<Warning>
  Alchemy Smart Accounts return a `userOpHash` instead of a standard transaction
  hash. You must use `waitForUserOperationReceipt()` instead of wagmi's
  `useWaitForTransactionReceipt`.
</Warning>

### Imports

```ts theme={null}
// SDK imports
import {
  prepareDepositAuthorization,
  prepareDeposit,
  isApprovalAuth,
  isAlreadyApprovedAuth,
  YieldType,
} from '@paxoslabs/amplify-sdk'

// Your Alchemy provider hook (implementation depends on your setup)
import { useAlchemySmartAccount } from '@/providers/alchemy-smart-account-provider'

// Viem imports
import { encodeFunctionData, formatUnits } from 'viem'
```

### useAlchemyDeposit Hook

```tsx theme={null}
// src/hooks/useAlchemyDeposit.ts
import { useState, useCallback } from 'react'
import { useAlchemySmartAccount } from '@/providers/alchemy-smart-account-provider'
import { encodeFunctionData, formatUnits } from 'viem'
import {
  prepareDepositAuthorization,
  prepareDeposit,
  isApprovalAuth,
  isAlreadyApprovedAuth,
  YieldType,
} from '@paxoslabs/amplify-sdk'

interface DepositParams {
  depositAsset: `0x${string}`
  amount: bigint
  signerAddress: `0x${string}`
  vaultName: string
}

export function useAlchemyDeposit() {
  const { client: alchemyClient } = useAlchemySmartAccount()
  const [status, setStatus] = useState<string>('')
  const [isLoading, setIsLoading] = useState(false)

  const deposit = useCallback(
    async ({
      depositAsset,
      amount,
      signerAddress,
      vaultName,
    }: DepositParams) => {
      if (!alchemyClient) {
        throw new Error('Alchemy client not connected')
      }

      setIsLoading(true)

      try {
        const params = {
          vaultName,
          depositAsset,
          depositAmount: formatUnits(amount, 6),
          to: signerAddress,
          chainId: 1,
          forceMethod: 'approval' as const,
        }

        // Step 1: Prepare authorization and deposit data
        setStatus('Preparing transaction...')
        const auth = await prepareDepositAuthorization(params)
        const prepared = await prepareDeposit(params)

        // Step 2: Batch via sendTransactions (ERC-4337 UserOperation)
        if (isApprovalAuth(auth)) {
          setStatus('Please confirm in your wallet...')

          const userOpHash = await alchemyClient.sendTransactions({
            requests: [
              {
                to: auth.txData.address,
                data: encodeFunctionData({
                  abi: auth.txData.abi,
                  functionName: auth.txData.functionName,
                  args: auth.txData.args,
                }),
              },
              {
                to: prepared.txData.address,
                data: encodeFunctionData({
                  abi: prepared.txData.abi,
                  functionName: prepared.txData.functionName,
                  args: prepared.txData.args,
                }),
              },
            ],
          })

          // Note: This is a UserOperation hash, NOT a transaction hash
          setStatus('Waiting for confirmation...')
          const receipt = await alchemyClient.waitForUserOperationReceipt({
            hash: userOpHash,
          })

          setStatus('Deposit successful!')
          return receipt.receipt.transactionHash
        } else if (isAlreadyApprovedAuth(auth)) {
          setStatus('Please confirm in your wallet...')

          const userOpHash = await alchemyClient.sendTransactions({
            requests: [
              {
                to: prepared.txData.address,
                data: encodeFunctionData({
                  abi: prepared.txData.abi,
                  functionName: prepared.txData.functionName,
                  args: prepared.txData.args,
                }),
              },
            ],
          })

          setStatus('Waiting for confirmation...')
          const receipt = await alchemyClient.waitForUserOperationReceipt({
            hash: userOpHash,
          })

          setStatus('Deposit successful!')
          return receipt.receipt.transactionHash
        }
      } catch (error) {
        setStatus(
          `Error: ${error instanceof Error ? error.message : 'Unknown'}`
        )
        throw error
      } finally {
        setIsLoading(false)
      }
    },
    [alchemyClient]
  )

  return { deposit, status, isLoading }
}
```

### Key Characteristics

* Uses `alchemyClient.sendTransactions({ requests })` for batching
* Returns `userOpHash` (not a standard transaction hash)
* Must use `waitForUserOperationReceipt()` instead of wagmi's receipt hook
* Bundler submits the UserOperation to the network
* Gas can be sponsored via paymaster configuration

***

## USDT Special Handling

USDT requires special handling because its `approve()` function requires resetting to 0 before setting a new value if there's an existing non-zero allowance:

```ts theme={null}
// When current allowance is non-zero but insufficient,
// USDT requires resetting to 0 first
if (currentAllowance > 0n && currentAllowance < requiredAmount) {
  // Step 1: Reset approval to 0
  await sendApproval(tokenAddress, spender, 0n)
  // Step 2: Set new approval amount
  await sendApproval(tokenAddress, spender, requiredAmount)
  // Step 3: Deposit
  await sendDeposit(depositTxData)
}
```

<Info>
  When batching is not possible (e.g., non-smart-wallet USDT deposits with
  existing partial allowance), these three steps must be sent sequentially.
</Info>

***

## Token-Specific Behavior

| Token | Supports Permit | Best Flow                       |
| ----- | --------------- | ------------------------------- |
| USDC  | Yes             | Permit (EOA) or Batched (Smart) |
| USDG  | Yes             | Permit (EOA) or Batched (Smart) |
| pyUSD | Yes             | Permit (EOA) or Batched (Smart) |
| USD0  | Yes             | Permit (EOA) or Batched (Smart) |
| USDT  | **No**          | Batched approval + deposit      |

***

## Troubleshooting

| Issue                                   | Solution                                                   |
| --------------------------------------- | ---------------------------------------------------------- |
| "Smart wallet not connected"            | Ensure user has a smart wallet configured in Privy/Dynamic |
| UserOperation fails                     | Check gas estimation, paymaster may need funding           |
| `waitForUserOperationReceipt` times out | Bundler may be congested, increase timeout                 |
| Batched tx reverts                      | Check individual call data, one call may be failing        |

For standard EOA wallet examples, see [Deposits](/v0.5.2/intro/products/earn/developers/guides/deposits).
