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

# Lending

> Supply a single asset to a lending market through the Yield API, then withdraw it.

Supply an asset to a lending market and take it back out. One yield runs through every example: `base-usdc-aave-v3-lending`, Aave V3's USDC reserve on Base, found by filtering `GET /v1/yields` for `types=lending`, `network=base`, and `inputToken=USDC`. Add `providers=aave` if you already know the protocol you want. Swap in whatever `id` your own discovery step returns; see [Discover yields](/guides/earn/discover-yields).

<Note>
  Lending here is supply-side only. You supply an asset and earn the variable rate paid by borrowers; you are not borrowing against collateral. That's a separate product: the Borrow API.
</Note>

## Prerequisites

* A yield.xyz API key, sent as `x-api-key` on every request.
* A lending yield enabled for your project. See [Discover yields](/guides/earn/discover-yields).
* A signer for the target chain. This guide uses a generic `signAndBroadcast` placeholder; see [Transaction signing](/guides/transaction-signing/index) for chain-specific code.

## Discover and compare lending yields

Filter the catalog down to lending markets for one asset on one network:

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api.yield.xyz/v1/yields?types=lending&network=base&inputToken=USDC&providers=aave' \
    --header "x-api-key: $YIELD_API_KEY"
  ```

  ```typescript TypeScript theme={null}
  const params = new URLSearchParams({
    types: 'lending',
    network: 'base',
    inputToken: 'USDC',
    providers: 'aave',
  });
  const res = await fetch(`https://api.yield.xyz/v1/yields?${params}`, {
    headers: { 'x-api-key': process.env.YIELD_API_KEY! },
  });
  const { items } = await res.json();
  ```
</CodeGroup>

```json Response (excerpt) theme={null}
{
  "items": [
    {
      "id": "base-usdc-aave-v3-lending",
      "providerId": "aave",
      "rewardRate": {
        "total": 0.0431,
        "rateType": "APY",
        "components": [{ "rate": 0.0431, "rateType": "APY", "yieldSource": "lending" }]
      },
      "status": { "enter": true, "exit": true },
      "state": { "capacityState": { "current": "180483062.93", "max": "230000000.00", "remaining": "49516937.07" } }
    }
  ]
}
```

For lending, compare a small set of fields across results rather than the full object:

* `rewardRate.total` is a live variable supply rate, expressed as a decimal fraction (`0.0431` is 4.31%) with `rateType` telling you APR or APY. It moves with pool utilization.
* `status.enter` must be `true` before you offer the yield. A reserve that is frozen or paying zero interest reports `enter: false` here.
* `state.capacityState.remaining`, when present, is how much more the reserve can accept before its supply cap. `state` is optional on the yield object: check it exists before reading `capacityState` off it, since not every yield type populates it.
* `rewardRate.components[]` breaks the total down by source, each entry with its own `rate`, `yieldSource`, and `token`. Here the only component is the `lending` rate itself; a reserve that also pays an incentive token lists it as an additional component.

## Read the enter schema

Never hardcode `{ amount }`. Read `mechanics.arguments.enter.fields` from `GET /v1/yields/{yieldId}` and build the request from it, the same pattern as [Enter and exit flows](/guides/earn/enter-exit-flows#build-the-enter-arguments-from-the-schema):

```json Response (excerpt) theme={null}
{
  "mechanics": {
    "arguments": {
      "enter": {
        "fields": [
          { "name": "amount", "type": "string", "required": true },
          { "name": "receiverAddress", "type": "string", "required": false },
          { "name": "executionMode", "type": "enum", "required": false, "options": ["individual", "batched"] }
        ]
      }
    }
  }
}
```

Only `amount` is required. `receiverAddress` redirects the aTokens to a different address than the signer. `executionMode` is covered below.

## Enter the position

<Steps>
  <Step title="Request the enter action">
    ```bash cURL theme={null}
    curl --request POST \
      --url https://api.yield.xyz/v1/actions/enter \
      --header "x-api-key: $YIELD_API_KEY" \
      --header 'content-type: application/json' \
      --data '{
        "yieldId": "base-usdc-aave-v3-lending",
        "address": "0xYOUR_WALLET_ADDRESS",
        "arguments": { "amount": "100" }
      }'
    ```

    ```json Response (excerpt) theme={null}
    {
      "id": "action_123abc",
      "executionPattern": "synchronous",
      "transactions": [
        { "id": "tx_123abc", "stepIndex": 0, "type": "APPROVAL", "status": "SKIPPED" },
        { "id": "tx_456def", "stepIndex": 1, "type": "SUPPLY", "status": "CREATED" }
      ]
    }
    ```

    The approval transaction comes back `SKIPPED` when an existing ERC-20 allowance already covers `amount`; otherwise it's `CREATED` and has to be signed like the supply transaction.
  </Step>

  <Step title="Sign and submit, in order">
    Follow [Enter and exit flows](/guides/earn/enter-exit-flows#enter-a-position) for the full branch, sign, submit, and poll loop: drive `transactions` in `stepIndex` order, skip anything already `SKIPPED` or otherwise terminal, and poll each to a terminal status before moving on.
  </Step>
</Steps>

### Batched execution for smart accounts

When `executionMode: "batched"` is set in `arguments`, the enter (or exit) action returns a single `BATCH`-type transaction instead of separate approval and supply steps: its `unsignedTransaction` is a JSON array of `{ to, data, value }` calls meant to be sent together as one smart-account user operation, rather than two sequential EOA transactions. Only use it with a signer that can execute a batch of calls in one operation.

## Confirm the position

Once the supply transaction confirms, read the balance:

```bash cURL theme={null}
curl --request POST \
  --url https://api.yield.xyz/v1/yields/base-usdc-aave-v3-lending/balances \
  --header "x-api-key: $YIELD_API_KEY" \
  --header 'content-type: application/json' \
  --data '{ "address": "0xYOUR_WALLET_ADDRESS" }'
```

```json Response (excerpt) theme={null}
{
  "balances": [
    { "type": "active", "amount": "100", "isEarning": true, "pendingActions": [] }
  ]
}
```

Aave mints its aToken directly to your address, and the aToken rebases: `amount` on this `active` balance grows on its own as interest accrues, with no separate `pricePerShare` step and no claim action to run. There's nothing to poll here beyond re-reading balances; interest is reflected the next time you read the position, not delivered through a `pendingActions` entry. See [Balances and positions](/documentation/earn/core-concepts/balances-positions) for the full balance model.

## Exit the position

Exit with either a specific `amount` or `useMaxAmount` to withdraw everything:

```bash cURL theme={null}
curl --request POST \
  --url https://api.yield.xyz/v1/actions/exit \
  --header "x-api-key: $YIELD_API_KEY" \
  --header 'content-type: application/json' \
  --data '{
    "yieldId": "base-usdc-aave-v3-lending",
    "address": "0xYOUR_WALLET_ADDRESS",
    "arguments": { "useMaxAmount": true }
  }'
```

Aave's exit is a single `WITHDRAW` transaction with no cooldown: sign and submit it the same way as enter, and the underlying asset settles as soon as that transaction confirms. Not every lending integration exits this directly; check the returned balance's `pendingActions` after any exit, as [Enter and exit flows](/guides/earn/enter-exit-flows#exit-a-position) covers.

## Errors that block the task

| Condition | What you see | What to do |
| - | - | - |
| Reserve frozen, or paying zero supply rate | `status.enter` is `false`; calling enter anyway returns `412 Precondition Failed` | Check `status.enter` before offering the yield; don't rely on the action call to reject it |
| Supply cap reached | Not blocked by the API; the `SUPPLY` transaction reverts on-chain | Read `state.capacityState.remaining` before offering a large deposit, and surface the on-chain revert if it still happens |
| Yield not enabled for your project | `400 Bad Request` from any per-yield call | Re-run discovery instead of calling an unverified `yieldId`; see [Discover yields](/guides/earn/discover-yields) |

## Other lending integrations

Swapping the provider, for example to Compound or Spark, changes what you read and sign, not the shape of the task: re-read `mechanics.arguments` for that yield rather than assuming Aave's fields, and check its exit behavior, since not every market exits in a single transaction the way Aave does. Some lending integrations are non-EVM, for example Kamino on Solana, which signs and submits through [Solana signing](/guides/transaction-signing/solana) instead of EVM transactions. See [Lending](/documentation/earn/defi/lending/index) for the full list of covered protocols.

## Next steps

<Columns cols={2}>
  <Card title="Enter and exit flows" icon="arrow-right-arrow-left" href="/guides/earn/enter-exit-flows">
    The full branch, sign, submit, and poll loop reused above.
  </Card>

  <Card title="Tracking balances" icon="wallet" href="/guides/earn/tracking-balances">
    Build portfolio views across multiple lending positions.
  </Card>
</Columns>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.