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

# Morpho

> Supply USDC to Morpho Vaults and enable yield for your users

This guide explains how to supply tokens to <a href="https://docs.morpho.org" target="_blank">Morpho</a> Vaults and earn yield using Crossmint wallets. Morpho is a decentralized lending protocol deployed across multiple EVM chains, offering ERC-4626 compliant vaults that optimize yield across lending markets.

## Prerequisites

To use Morpho Vaults with Crossmint wallets, you need:

* A Crossmint [wallet](/wallets/guides/create-wallet) on Base
* A **production** **API Key** with the scope: `wallets:transactions.create` (create in the <a href="https://www.crossmint.com/console" target="_blank">Production Console</a>)

This guide uses USDC on Base mainnet as an example. To follow along, you will also need:

* USDC on Base mainnet in the wallet
* ETH on Base for gas fees (not required if [gas sponsorship](/wallets/guides/gas-sponsorship) is enabled)

You can adapt the code to supply other tokens or use vaults on other chains where Morpho is deployed (Ethereum, Arbitrum, Polygon, and others).

## Discover Vaults

Morpho exposes a public <a href="https://api.morpho.org/graphql" target="_blank">GraphQL API</a> for discovering available vaults, their APYs, and total deposits. No API key is required.

```typescript theme={null}
const MORPHO_API = "https://api.morpho.org/graphql";

const query = `{
  vaultV2s(
    first: 10,
    where: { chainId_in: [8453], assetAddress_in: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"] },
    orderBy: TotalAssetsUsd,
    orderDirection: Desc
  ) {
    items {
      address
      name
      avgNetApy
      totalAssetsUsd
    }
  }
}`;

const response = await fetch(MORPHO_API, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ query }),
});
const data = await response.json();
const vaults = data.data.vaultV2s.items;
```

## Supply Tokens

High-level steps to supply tokens to a Morpho Vault:

1. Query the Morpho API to select a vault
2. Approve the vault contract to spend your tokens (ERC-20 `approve`)
3. Deposit tokens into the vault (ERC-4626 `deposit`)

<Warning>
  Morpho Vaults operate on mainnet only. The wallet must hold sufficient tokens on the target chain and ETH for gas fees.
</Warning>

<Tabs>
  <Tab title="React">
    ```typescript theme={null}
    import { useWallet, EVMWallet } from "@crossmint/client-sdk-react-ui";
    import { encodeFunctionData, erc20Abi, parseUnits } from "viem";

    const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
    const USDC_DECIMALS = 6;
    const VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9"; // Steakhouse USDC on Base

    const vaultAbi = [
        {
            name: "deposit",
            type: "function",
            inputs: [
                { name: "assets", type: "uint256" },
                { name: "receiver", type: "address" },
            ],
            outputs: [{ name: "shares", type: "uint256" }],
            stateMutability: "nonpayable",
        },
    ] as const;

    export function SupplyComponent() {
        const { wallet } = useWallet();

        async function supply(amount: string) {
            if (!wallet) return;
            const evmWallet = EVMWallet.from(wallet);
            const depositAmount = parseUnits(amount, USDC_DECIMALS);

            // 1. Approve the vault to spend USDC
            await evmWallet.sendTransaction({
                to: USDC_ADDRESS,
                data: encodeFunctionData({
                    abi: erc20Abi,
                    functionName: "approve",
                    args: [VAULT_ADDRESS, depositAmount],
                }),
            });

            // 2. Deposit USDC into the Morpho vault
            const tx = await evmWallet.sendTransaction({
                to: VAULT_ADDRESS,
                data: encodeFunctionData({
                    abi: vaultAbi,
                    functionName: "deposit",
                    args: [depositAmount, wallet.address as `0x${string}`],
                }),
            });

            console.log("Supply complete!", tx.hash);
        }

        return (
            <button onClick={() => supply("10")}>Supply 10 USDC</button>
        );
    }
    ```

    See the [React SDK reference](/sdk-reference/wallets/react/hooks#wallet-methods) for more details.
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { CrossmintWallets, createCrossmint, EVMWallet } from "@crossmint/wallets-sdk";
    import { encodeFunctionData, erc20Abi, parseUnits } from "viem";

    const crossmint = createCrossmint({
        apiKey: "YOUR_SERVER_API_KEY",
    });
    const crossmintWallets = CrossmintWallets.from(crossmint);

    const wallet = await crossmintWallets.getWallet(
        "email:user@example.com",
        { chain: "base" }
    );
    if (!wallet) throw new Error("Wallet not found");
    await wallet.useSigner({ type: "email", email: "user@example.com" });
    const evmWallet = EVMWallet.from(wallet);

    const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
    const USDC_DECIMALS = 6;
    const VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9"; // Steakhouse USDC on Base
    const amount = "10";

    const vaultAbi = [
        {
            name: "deposit",
            type: "function",
            inputs: [
                { name: "assets", type: "uint256" },
                { name: "receiver", type: "address" },
            ],
            outputs: [{ name: "shares", type: "uint256" }],
            stateMutability: "nonpayable",
        },
    ] as const;

    const depositAmount = parseUnits(amount, USDC_DECIMALS);

    // 1. Approve the vault to spend USDC
    await evmWallet.sendTransaction({
        to: USDC_ADDRESS,
        data: encodeFunctionData({
            abi: erc20Abi,
            functionName: "approve",
            args: [VAULT_ADDRESS, depositAmount],
        }),
    });

    // 2. Deposit USDC into the Morpho vault
    const tx = await evmWallet.sendTransaction({
        to: VAULT_ADDRESS,
        data: encodeFunctionData({
            abi: vaultAbi,
            functionName: "deposit",
            args: [depositAmount, wallet.address as `0x${string}`],
        }),
    });

    console.log("Supply complete!", tx.hash);
    ```
  </Tab>

  <Tab title="React Native">
    ```typescript theme={null}
    import { useWallet, EVMWallet } from "@crossmint/client-sdk-react-native-ui";
    import { View, Button } from "react-native";
    import { encodeFunctionData, erc20Abi, parseUnits } from "viem";

    const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
    const USDC_DECIMALS = 6;
    const VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9"; // Steakhouse USDC on Base

    const vaultAbi = [
        {
            name: "deposit",
            type: "function",
            inputs: [
                { name: "assets", type: "uint256" },
                { name: "receiver", type: "address" },
            ],
            outputs: [{ name: "shares", type: "uint256" }],
            stateMutability: "nonpayable",
        },
    ] as const;

    export function SupplyComponent() {
        const { wallet } = useWallet();

        async function supply(amount: string) {
            if (!wallet) return;
            const evmWallet = EVMWallet.from(wallet);
            const depositAmount = parseUnits(amount, USDC_DECIMALS);

            // 1. Approve the vault to spend USDC
            await evmWallet.sendTransaction({
                to: USDC_ADDRESS,
                data: encodeFunctionData({
                    abi: erc20Abi,
                    functionName: "approve",
                    args: [VAULT_ADDRESS, depositAmount],
                }),
            });

            // 2. Deposit USDC into the Morpho vault
            const tx = await evmWallet.sendTransaction({
                to: VAULT_ADDRESS,
                data: encodeFunctionData({
                    abi: vaultAbi,
                    functionName: "deposit",
                    args: [depositAmount, wallet.address as `0x${string}`],
                }),
            });

            console.log("Supply complete!", tx.hash);
        }

        return (
            <View>
                <Button title="Supply 10 USDC" onPress={() => supply("10")} />
            </View>
        );
    }
    ```

    See the [React Native SDK reference](/sdk-reference/wallets/react-native/hooks#wallet-methods) for more details.
  </Tab>

  <Tab title="Swift">
    ```swift theme={null}
    import CrossmintClient
    import Wallet
    import Foundation

    let sdk = CrossmintSDK.shared

    let wallet = try await sdk.crossmintWallets
        .getOrCreateWallet(
            chain: .base,
            signer: .email(email: "user@example.com")
        )

    let evmWallet = try EVMWallet.from(wallet: wallet)

    let usdcAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
    let vaultAddress = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9"
    let amount = "10000000" // 10 USDC (6 decimals)

    // 1. Approve the vault to spend USDC
    //    Encodes ERC-20 approve(address,uint256) — selector 0x095ea7b3
    func leftPad(_ str: String, toLength len: Int) -> String {
        String(repeating: "0", count: max(0, len - str.count)) + str
    }

    let addr = leftPad(String(vaultAddress.dropFirst(2)), toLength: 64)
    let amt = leftPad(String(UInt64(amount)!, radix: 16), toLength: 64)
    let approveData = "0x095ea7b3" + addr + amt
    let _ = try await evmWallet.sendTransaction(
        to: usdcAddress,
        value: "0",
        data: approveData
    )

    // 2. Deposit USDC into the Morpho vault
    //    Encodes ERC-4626 deposit(uint256,address) — selector 0x6e553f65
    let walletAddr = leftPad(String(wallet.address.dropFirst(2)), toLength: 64)
    let depositData = "0x6e553f65" + amt + walletAddr
    let result = try await evmWallet.sendTransaction(
        to: vaultAddress,
        value: "0",
        data: depositData
    )
    ```

    See the [Swift SDK reference](/sdk-reference/wallets/swift/classes/evmwallet) for more details.
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.crossmint.kotlin.compose.LocalCrossmintSDK
    import com.crossmint.kotlin.types.EVMChain
    import com.crossmint.kotlin.types.SignerData
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.runBlocking
    import kotlinx.coroutines.withContext
    import kotlinx.serialization.json.Json
    import kotlinx.serialization.json.buildJsonObject
    import kotlinx.serialization.json.jsonObject
    import kotlinx.serialization.json.jsonPrimitive
    import kotlinx.serialization.json.put
    import kotlinx.serialization.json.putJsonArray
    import kotlinx.serialization.json.putJsonObject
    import java.net.HttpURLConnection
    import java.net.URL

    val API_KEY = "YOUR_API_KEY"
    val SIGNER_EMAIL = "user@example.com"
    val CROSSMINT_API = "https://www.crossmint.com/api/2025-06-09"
    val USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
    val VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9"
    val AMOUNT = "10000000" // 10 USDC (6 decimals)

    val sdk = LocalCrossmintSDK.current
    val wallet = sdk.crossmintWallets
        .getOrCreateWallet(EVMChain.Base, SignerData.Email(SIGNER_EMAIL))
        .getOrThrow()

    val json = Json { ignoreUnknownKeys = true }

    suspend fun sendEvmTransaction(to: String, data: String, value: String = "0"): String {
        val body = buildJsonObject {
            putJsonObject("params") {
                put("chain", "base")
                put("signer", "email:$SIGNER_EMAIL")
                putJsonArray("calls") {
                    add(buildJsonObject {
                        put("to", to)
                        put("value", value)
                        put("data", data)
                    })
                }
            }
        }.toString()
        val responseText = withContext(Dispatchers.IO) {
            val conn = URL("$CROSSMINT_API/wallets/${wallet.address}/transactions")
                .openConnection() as HttpURLConnection
            conn.requestMethod = "POST"
            conn.setRequestProperty("X-API-KEY", API_KEY)
            conn.setRequestProperty("Content-Type", "application/json")
            conn.doOutput = true
            conn.outputStream.write(body.toByteArray())
            val code = conn.responseCode
            if (code !in 200..299) {
                val err = conn.errorStream?.bufferedReader()?.readText()
                error("Transaction request failed ($code): $err")
            }
            conn.inputStream.bufferedReader().readText()
        }
        val transactionId = json.parseToJsonElement(responseText)
            .jsonObject["id"]!!.jsonPrimitive.content
        val tx = wallet.approve(transactionId).getOrThrow()
        return tx.onChain.txId ?: tx.onChain.userOperationHash
            ?: error("Transaction completed but no hash was returned")
    }

    // 1. Approve the vault to spend USDC
    //    Encodes ERC-20 approve(address,uint256) — selector 0x095ea7b3
    val addr = VAULT_ADDRESS.removePrefix("0x").padStart(64, '0')
    val amt = AMOUNT.toLong().toString(16).padStart(64, '0')

    // 2. Deposit USDC into the Morpho vault
    //    Encodes ERC-4626 deposit(uint256,address) — selector 0x6e553f65
    val walletAddr = wallet.address.removePrefix("0x").padStart(64, '0')

    runBlocking {
        sendEvmTransaction(to = USDC_ADDRESS, data = "0x095ea7b3$addr$amt")
        val txHash = sendEvmTransaction(to = VAULT_ADDRESS, data = "0x6e553f65$amt$walletAddr")
        println("Supply complete! $txHash")
    }
    ```

    See the [Kotlin SDK reference](/sdk-reference/wallets/kotlin/classes/wallet) for more details.
  </Tab>

  <Tab title="REST">
    Transactions must be approved by one of the wallet's [signers](/wallets/concepts/wallet-signers).
    The SDK handles this automatically, but with the REST API you must [approve the transaction](/api-reference/wallets/approve-transaction) to complete it.

    <Steps>
      <Step title="Create the ERC-20 approve transaction">
        Send an ERC-20 `approve` transaction to allow the Morpho vault to pull tokens.

        <CodeGroup>
          ```bash cURL theme={null}
          curl --request POST \
              --url https://www.crossmint.com/api/2025-06-09/wallets/WALLET_ADDRESS/transactions \
              --header 'Content-Type: application/json' \
              --header 'X-API-KEY: YOUR_API_KEY' \
              --data '{
                  "params": {
                      "chain": "base",
                      "signer": "email:USER_EMAIL",
                      "calls": [{
                          "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                          "value": "0",
                          "data": "0x095ea7b3000000000000000000000000beef0e0834849acc03f0089f01f4f1eeb06873c90000000000000000000000000000000000000000000000000000000000989680"
                      }]
                  }
              }'
          ```

          ```js Node.js theme={null}
          const walletAddress = "YOUR_WALLET_ADDRESS";

          const response = await fetch(
              `https://www.crossmint.com/api/2025-06-09/wallets/${walletAddress}/transactions`,
              {
                  method: "POST",
                  headers: {
                      "Content-Type": "application/json",
                      "X-API-KEY": "YOUR_API_KEY",
                  },
                  body: JSON.stringify({
                      params: {
                          chain: "base",
                          signer: "email:user@example.com",
                          calls: [{
                              to: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                              value: "0",
                              data: "0x095ea7b3000000000000000000000000beef0e0834849acc03f0089f01f4f1eeb06873c90000000000000000000000000000000000000000000000000000000000989680",
                          }],
                      },
                  }),
              }
          );
          const { id } = await response.json();
          ```

          ```python Python theme={null}
          import requests

          wallet_address = "YOUR_WALLET_ADDRESS"

          response = requests.post(
              f"https://www.crossmint.com/api/2025-06-09/wallets/{wallet_address}/transactions",
              headers={
                  "Content-Type": "application/json",
                  "X-API-KEY": "YOUR_API_KEY",
              },
              json={
                  "params": {
                      "chain": "base",
                      "signer": "email:user@example.com",
                      "calls": [{
                          "to": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                          "value": "0",
                          "data": "0x095ea7b3000000000000000000000000beef0e0834849acc03f0089f01f4f1eeb06873c90000000000000000000000000000000000000000000000000000000000989680",
                      }],
                  },
              },
          )
          transaction_id = response.json()["id"]
          ```
        </CodeGroup>
      </Step>

      <Step title="Approve the transaction">
        Use the transaction ID from the previous step to [approve the transaction](/api-reference/wallets/approve-transaction) so it settles on-chain.

        <CodeGroup>
          ```bash cURL theme={null}
          curl --request POST \
              --url https://www.crossmint.com/api/2025-06-09/wallets/WALLET_ADDRESS/transactions/TRANSACTION_ID/approve \
              --header 'Content-Type: application/json' \
              --header 'X-API-KEY: YOUR_API_KEY'
          ```

          ```js Node.js theme={null}
          await fetch(
              `https://www.crossmint.com/api/2025-06-09/wallets/${walletAddress}/transactions/${id}/approve`,
              {
                  method: "POST",
                  headers: {
                      "Content-Type": "application/json",
                      "X-API-KEY": "YOUR_API_KEY",
                  },
              }
          );
          ```

          ```python Python theme={null}
          requests.post(
              f"https://www.crossmint.com/api/2025-06-09/wallets/{wallet_address}/transactions/{transaction_id}/approve",
              headers={
                  "Content-Type": "application/json",
                  "X-API-KEY": "YOUR_API_KEY",
              },
          )
          ```
        </CodeGroup>
      </Step>

      <Step title="Create the deposit transaction">
        Send an ERC-4626 `deposit` transaction to the Morpho vault. The `data` field encodes `deposit(uint256 assets, address receiver)` where:

        * `0x6e553f65` is the function selector
        * Next 32 bytes: the deposit amount (10 USDC = `0x989680`)
        * Last 32 bytes: the receiver wallet address, zero-padded to 32 bytes

        <CodeGroup>
          ```bash cURL theme={null}
          curl --request POST \
              --url https://www.crossmint.com/api/2025-06-09/wallets/WALLET_ADDRESS/transactions \
              --header 'Content-Type: application/json' \
              --header 'X-API-KEY: YOUR_API_KEY' \
              --data '{
                  "params": {
                      "chain": "base",
                      "signer": "email:USER_EMAIL",
                      "calls": [{
                          "to": "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9",
                          "value": "0",
                          "data": "0x6e553f6500000000000000000000000000000000000000000000000000000000009896800000000000000000000000001234567890abcdef1234567890abcdef12345678"
                      }]
                  }
              }'
          ```

          ```js Node.js theme={null}
          const walletAddress = "YOUR_WALLET_ADDRESS";

          const depositResponse = await fetch(
              `https://www.crossmint.com/api/2025-06-09/wallets/${walletAddress}/transactions`,
              {
                  method: "POST",
                  headers: {
                      "Content-Type": "application/json",
                      "X-API-KEY": "YOUR_API_KEY",
                  },
                  body: JSON.stringify({
                      params: {
                          chain: "base",
                          signer: "email:user@example.com",
                          calls: [{
                              to: "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9",
                              value: "0",
                              data: `0x6e553f650000000000000000000000000000000000000000000000000000000000989680${walletAddress.slice(2).padStart(64, "0")}`,
                          }],
                      },
                  }),
              }
          );
          const { id: depositId } = await depositResponse.json();
          ```

          ```python Python theme={null}
          wallet_address = "YOUR_WALLET_ADDRESS"

          deposit_response = requests.post(
              f"https://www.crossmint.com/api/2025-06-09/wallets/{wallet_address}/transactions",
              headers={
                  "Content-Type": "application/json",
                  "X-API-KEY": "YOUR_API_KEY",
              },
              json={
                  "params": {
                      "chain": "base",
                      "signer": "email:user@example.com",
                      "calls": [{
                          "to": "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9",
                          "value": "0",
                          "data": f"0x6e553f650000000000000000000000000000000000000000000000000000000000989680{wallet_address[2:].zfill(64)}",
                      }],
                  },
              },
          )
          deposit_id = deposit_response.json()["id"]
          ```
        </CodeGroup>
      </Step>

      <Step title="Approve the deposit transaction">
        Approve the deposit transaction using the ID returned in the previous step.

        <CodeGroup>
          ```bash cURL theme={null}
          curl --request POST \
              --url https://www.crossmint.com/api/2025-06-09/wallets/WALLET_ADDRESS/transactions/TRANSACTION_ID/approve \
              --header 'Content-Type: application/json' \
              --header 'X-API-KEY: YOUR_API_KEY'
          ```

          ```js Node.js theme={null}
          await fetch(
              `https://www.crossmint.com/api/2025-06-09/wallets/${walletAddress}/transactions/${depositId}/approve`,
              {
                  method: "POST",
                  headers: {
                      "Content-Type": "application/json",
                      "X-API-KEY": "YOUR_API_KEY",
                  },
              }
          );
          ```

          ```python Python theme={null}
          requests.post(
              f"https://www.crossmint.com/api/2025-06-09/wallets/{wallet_address}/transactions/{deposit_id}/approve",
              headers={
                  "Content-Type": "application/json",
                  "X-API-KEY": "YOUR_API_KEY",
              },
          )
          ```
        </CodeGroup>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Withdraw Tokens

To withdraw tokens from a Morpho Vault, call the ERC-4626 `withdraw` function. This redeems your shares and returns the underlying tokens.

<Tabs>
  <Tab title="React">
    ```typescript theme={null}
    import { useWallet, EVMWallet } from "@crossmint/client-sdk-react-ui";
    import { encodeFunctionData, parseUnits } from "viem";

    const VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9";
    const USDC_DECIMALS = 6;

    const withdrawAbi = [
        {
            name: "withdraw",
            type: "function",
            inputs: [
                { name: "assets", type: "uint256" },
                { name: "receiver", type: "address" },
                { name: "owner", type: "address" },
            ],
            outputs: [{ name: "shares", type: "uint256" }],
            stateMutability: "nonpayable",
        },
    ] as const;

    export function WithdrawComponent() {
        const { wallet } = useWallet();

        async function withdraw(amount: string) {
            if (!wallet) return;
            const evmWallet = EVMWallet.from(wallet);
            const walletAddress = wallet.address as `0x${string}`;

            const tx = await evmWallet.sendTransaction({
                to: VAULT_ADDRESS,
                data: encodeFunctionData({
                    abi: withdrawAbi,
                    functionName: "withdraw",
                    args: [parseUnits(amount, USDC_DECIMALS), walletAddress, walletAddress],
                }),
            });

            console.log("Withdrawal complete!", tx.hash);
        }

        return (
            <button onClick={() => withdraw("10")}>Withdraw 10 USDC</button>
        );
    }
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { CrossmintWallets, createCrossmint, EVMWallet } from "@crossmint/wallets-sdk";
    import { encodeFunctionData, parseUnits } from "viem";

    const crossmint = createCrossmint({
        apiKey: "YOUR_SERVER_API_KEY",
    });
    const crossmintWallets = CrossmintWallets.from(crossmint);

    const wallet = await crossmintWallets.getWallet(
        "email:user@example.com",
        { chain: "base" }
    );
    if (!wallet) throw new Error("Wallet not found");
    await wallet.useSigner({ type: "email", email: "user@example.com" });
    const evmWallet = EVMWallet.from(wallet);

    const VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9";
    const USDC_DECIMALS = 6;
    const amount = "10";
    const walletAddress = wallet.address as `0x${string}`;

    const withdrawAbi = [
        {
            name: "withdraw",
            type: "function",
            inputs: [
                { name: "assets", type: "uint256" },
                { name: "receiver", type: "address" },
                { name: "owner", type: "address" },
            ],
            outputs: [{ name: "shares", type: "uint256" }],
            stateMutability: "nonpayable",
        },
    ] as const;

    const tx = await evmWallet.sendTransaction({
        to: VAULT_ADDRESS,
        data: encodeFunctionData({
            abi: withdrawAbi,
            functionName: "withdraw",
            args: [parseUnits(amount, USDC_DECIMALS), walletAddress, walletAddress],
        }),
    });

    console.log("Withdrawal complete!", tx.hash);
    ```
  </Tab>

  <Tab title="React Native">
    ```typescript theme={null}
    import { useWallet, EVMWallet } from "@crossmint/client-sdk-react-native-ui";
    import { View, Button } from "react-native";
    import { encodeFunctionData, parseUnits } from "viem";

    const VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9";
    const USDC_DECIMALS = 6;

    const withdrawAbi = [
        {
            name: "withdraw",
            type: "function",
            inputs: [
                { name: "assets", type: "uint256" },
                { name: "receiver", type: "address" },
                { name: "owner", type: "address" },
            ],
            outputs: [{ name: "shares", type: "uint256" }],
            stateMutability: "nonpayable",
        },
    ] as const;

    export function WithdrawComponent() {
        const { wallet } = useWallet();

        async function withdraw(amount: string) {
            if (!wallet) return;
            const evmWallet = EVMWallet.from(wallet);
            const walletAddress = wallet.address as `0x${string}`;

            const tx = await evmWallet.sendTransaction({
                to: VAULT_ADDRESS,
                data: encodeFunctionData({
                    abi: withdrawAbi,
                    functionName: "withdraw",
                    args: [parseUnits(amount, USDC_DECIMALS), walletAddress, walletAddress],
                }),
            });

            console.log("Withdrawal complete!", tx.hash);
        }

        return (
            <View>
                <Button title="Withdraw 10 USDC" onPress={() => withdraw("10")} />
            </View>
        );
    }
    ```

    See the [React Native SDK reference](/sdk-reference/wallets/react-native/hooks#wallet-methods) for more details.
  </Tab>

  <Tab title="Swift">
    ```swift theme={null}
    import CrossmintClient
    import Wallet
    import Foundation

    let sdk = CrossmintSDK.shared

    let wallet = try await sdk.crossmintWallets
        .getOrCreateWallet(
            chain: .base,
            signer: .email(email: "user@example.com")
        )

    let evmWallet = try EVMWallet.from(wallet: wallet)

    let vaultAddress = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9"
    let amount = "10000000" // 10 USDC (6 decimals)

    func leftPad(_ str: String, toLength len: Int) -> String {
        String(repeating: "0", count: max(0, len - str.count)) + str
    }

    // Encodes ERC-4626 withdraw(uint256,address,address) — selector 0xb460af94
    let amt = leftPad(String(UInt64(amount)!, radix: 16), toLength: 64)
    let walletAddr = leftPad(String(wallet.address.dropFirst(2)), toLength: 64)
    let withdrawData = "0xb460af94" + amt + walletAddr + walletAddr
    let result = try await evmWallet.sendTransaction(
        to: vaultAddress,
        value: "0",
        data: withdrawData
    )
    ```

    See the [Swift SDK reference](/sdk-reference/wallets/swift/classes/evmwallet) for more details.
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.crossmint.kotlin.compose.LocalCrossmintSDK
    import com.crossmint.kotlin.types.EVMChain
    import com.crossmint.kotlin.types.SignerData
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.runBlocking
    import kotlinx.coroutines.withContext
    import kotlinx.serialization.json.Json
    import kotlinx.serialization.json.buildJsonObject
    import kotlinx.serialization.json.jsonObject
    import kotlinx.serialization.json.jsonPrimitive
    import kotlinx.serialization.json.put
    import kotlinx.serialization.json.putJsonArray
    import kotlinx.serialization.json.putJsonObject
    import java.net.HttpURLConnection
    import java.net.URL

    val API_KEY = "YOUR_API_KEY"
    val SIGNER_EMAIL = "user@example.com"
    val CROSSMINT_API = "https://www.crossmint.com/api/2025-06-09"
    val VAULT_ADDRESS = "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9"
    val AMOUNT = "10000000" // 10 USDC (6 decimals)

    val sdk = LocalCrossmintSDK.current
    val wallet = sdk.crossmintWallets
        .getOrCreateWallet(EVMChain.Base, SignerData.Email(SIGNER_EMAIL))
        .getOrThrow()

    val json = Json { ignoreUnknownKeys = true }

    suspend fun sendEvmTransaction(to: String, data: String, value: String = "0"): String {
        val body = buildJsonObject {
            putJsonObject("params") {
                put("chain", "base")
                put("signer", "email:$SIGNER_EMAIL")
                putJsonArray("calls") {
                    add(buildJsonObject {
                        put("to", to)
                        put("value", value)
                        put("data", data)
                    })
                }
            }
        }.toString()
        val responseText = withContext(Dispatchers.IO) {
            val conn = URL("$CROSSMINT_API/wallets/${wallet.address}/transactions")
                .openConnection() as HttpURLConnection
            conn.requestMethod = "POST"
            conn.setRequestProperty("X-API-KEY", API_KEY)
            conn.setRequestProperty("Content-Type", "application/json")
            conn.doOutput = true
            conn.outputStream.write(body.toByteArray())
            val code = conn.responseCode
            if (code !in 200..299) {
                val err = conn.errorStream?.bufferedReader()?.readText()
                error("Transaction request failed ($code): $err")
            }
            conn.inputStream.bufferedReader().readText()
        }
        val transactionId = json.parseToJsonElement(responseText)
            .jsonObject["id"]!!.jsonPrimitive.content
        val tx = wallet.approve(transactionId).getOrThrow()
        return tx.onChain.txId ?: tx.onChain.userOperationHash
            ?: error("Transaction completed but no hash was returned")
    }

    // Encodes ERC-4626 withdraw(uint256,address,address) — selector 0xb460af94
    val amt = AMOUNT.toLong().toString(16).padStart(64, '0')
    val walletAddr = wallet.address.removePrefix("0x").padStart(64, '0')

    runBlocking {
        val txHash = sendEvmTransaction(to = VAULT_ADDRESS, data = "0xb460af94$amt$walletAddr$walletAddr")
        println("Withdrawal complete! $txHash")
    }
    ```

    See the [Kotlin SDK reference](/sdk-reference/wallets/kotlin/classes/wallet) for more details.
  </Tab>

  <Tab title="REST">
    Transactions must be approved by one of the wallet's [signers](/wallets/concepts/wallet-signers).
    The SDK handles this automatically, but with the REST API you must [approve the transaction](/api-reference/wallets/approve-transaction) to complete it.

    <Steps>
      <Step title="Create the withdraw transaction">
        Send an ERC-4626 `withdraw` transaction. The `data` field encodes `withdraw(uint256 assets, address receiver, address owner)` where:

        * `0xb460af94` is the function selector
        * Next 32 bytes: the withdraw amount (10 USDC = `0x989680`)
        * Next 32 bytes: the receiver address, zero-padded to 32 bytes
        * Last 32 bytes: the owner address (same as receiver), zero-padded to 32 bytes

        <CodeGroup>
          ```bash cURL theme={null}
          curl --request POST \
              --url https://www.crossmint.com/api/2025-06-09/wallets/WALLET_ADDRESS/transactions \
              --header 'Content-Type: application/json' \
              --header 'X-API-KEY: YOUR_API_KEY' \
              --data '{
                  "params": {
                      "chain": "base",
                      "signer": "email:USER_EMAIL",
                      "calls": [{
                          "to": "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9",
                          "value": "0",
                          "data": "0xb460af9400000000000000000000000000000000000000000000000000000000009896800000000000000000000000001234567890abcdef1234567890abcdef123456780000000000000000000000001234567890abcdef1234567890abcdef12345678"
                      }]
                  }
              }'
          ```

          ```js Node.js theme={null}
          const walletAddress = "YOUR_WALLET_ADDRESS";

          const response = await fetch(
              `https://www.crossmint.com/api/2025-06-09/wallets/${walletAddress}/transactions`,
              {
                  method: "POST",
                  headers: {
                      "Content-Type": "application/json",
                      "X-API-KEY": "YOUR_API_KEY",
                  },
                  body: JSON.stringify({
                      params: {
                          chain: "base",
                          signer: "email:user@example.com",
                          calls: [{
                              to: "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9",
                              value: "0",
                              data: `0xb460af940000000000000000000000000000000000000000000000000000000000989680${walletAddress.slice(2).padStart(64, "0")}${walletAddress.slice(2).padStart(64, "0")}`,
                          }],
                      },
                  }),
              }
          );
          const { id } = await response.json();
          ```

          ```python Python theme={null}
          import requests

          wallet_address = "YOUR_WALLET_ADDRESS"

          response = requests.post(
              f"https://www.crossmint.com/api/2025-06-09/wallets/{wallet_address}/transactions",
              headers={
                  "Content-Type": "application/json",
                  "X-API-KEY": "YOUR_API_KEY",
              },
              json={
                  "params": {
                      "chain": "base",
                      "signer": "email:user@example.com",
                      "calls": [{
                          "to": "0xbeef0e0834849aCC03f0089F01f4F1Eeb06873C9",
                          "value": "0",
                          "data": f"0xb460af940000000000000000000000000000000000000000000000000000000000989680{wallet_address[2:].zfill(64)}{wallet_address[2:].zfill(64)}",
                      }],
                  },
              },
          )
          transaction_id = response.json()["id"]
          ```
        </CodeGroup>
      </Step>

      <Step title="Approve the withdraw transaction">
        Approve the transaction using the ID returned in the previous step.

        <CodeGroup>
          ```bash cURL theme={null}
          curl --request POST \
              --url https://www.crossmint.com/api/2025-06-09/wallets/WALLET_ADDRESS/transactions/TRANSACTION_ID/approve \
              --header 'Content-Type: application/json' \
              --header 'X-API-KEY: YOUR_API_KEY'
          ```

          ```js Node.js theme={null}
          await fetch(
              `https://www.crossmint.com/api/2025-06-09/wallets/${walletAddress}/transactions/${id}/approve`,
              {
                  method: "POST",
                  headers: {
                      "Content-Type": "application/json",
                      "X-API-KEY": "YOUR_API_KEY",
                  },
              }
          );
          ```

          ```python Python theme={null}
          requests.post(
              f"https://www.crossmint.com/api/2025-06-09/wallets/{wallet_address}/transactions/{transaction_id}/approve",
              headers={
                  "Content-Type": "application/json",
                  "X-API-KEY": "YOUR_API_KEY",
              },
          )
          ```
        </CodeGroup>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Customizing the Vault

The example above uses the Steakhouse Prime Instant USDC vault on Base. You can adapt this to any Morpho vault by:

1. **Changing the vault address** — Use the [Discover Vaults](#discover-vaults) query to find vaults for different tokens or chains
2. **Changing the chain** — Morpho is deployed on Ethereum, Base, Arbitrum, Polygon, and other EVM chains. Update the `chainId_in` filter and the wallet chain accordingly.
3. **Changing the token** — Update the `assetAddress_in` filter and token decimals to match the vault's underlying asset

## Troubleshooting

<AccordionGroup>
  <Accordion title="Approve transaction reverts">
    Ensure the wallet holds enough of the token to cover the deposit amount.
  </Accordion>

  <Accordion title="Deposit transaction reverts">
    Verify the approval was confirmed before calling deposit. Check that the vault has not been paused.
  </Accordion>

  <Accordion title="Vault not found in API">
    Confirm the chain ID and asset address are correct. Some vaults may be delisted.
  </Accordion>

  <Accordion title="Shares not appearing">
    Vault shares are minted to the `receiver` address. Verify the wallet address is correct.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={3}>
  <Card title="Yield Strategies" icon="chart-line" href="/wallets/concepts/wallet-extensions/yield-introduction">
    Learn how vaults generate returns and which strategies fit your users.
  </Card>

  <Card title="Gas Sponsorship" icon="gas-pump" href="/wallets/guides/gas-sponsorship">
    Remove the ETH requirement for gas fees so users only need the supply token.
  </Card>

  <Card title="Morpho SDK" icon="book" href="https://docs.morpho.org/tools/offchain/sdks/morpho-sdk/">
    Explore advanced use cases like direct market lending and borrowing.
  </Card>
</CardGroup>
