# Repay

Endpoint reference for the 1delta API. Index: https://docs.1delta.io/llms.txt · every endpoint: https://docs.1delta.io/llms-full.txt

---

### GET /v1/actions/lending/repay

- operationId: `lending-repay`
- docs: https://docs.1delta.io/1delta-api/lending-repay/
- markdown: https://docs.1delta.io/1delta-api/lending-repay.md
- tags: Lending (Actions)

Repay

Build calldata for repaying a loan. Identify the market via `marketUid` (format: `lender:chainId:address`). Approval transactions are automatically filtered: if the user already has sufficient allowances, `permissions` (envelope) and `permissionTxns` (per-entry) will be empty. Pass `simulate=true` to include projected post-trade metrics. When on-chain debt data is available and `amount` covers the full debt, the API uses protocol-level max-repay mechanisms automatically. If `isAll=true` but `amount` is less than the debt, the API falls back to a partial repay to avoid reverts.

**Lista DAO fixed-term (brokered) markets:** pass `loanId` to target a specific loan (use the per-loan `loanId` from the user-positions response, or the `type(uint128).max` sentinel for the flexible/dynamic position). The broker repays interest-first plus an early-repayment penalty on not-yet-matured fixed loans and refunds any excess, so to fully close a loan fund `outstanding + accruedInterest + earlyRepayPenalty` from the per-loan `term`.

**Morpho Midnight fixed-term markets (`MORPHO_MIDNIGHT_<id>`) — repaying to avoid liquidation.** A Midnight loan is **zero-coupon and fixed-maturity**: you owe a *static face value* — the position's `debt` in loan-token units — repaid **1:1**, which does **not** grow over time. So the amount to repay is fixed and known up front (the continuous + settlement fees accrue on the *lender's* side and are **never** added to your debt), and there is **no early-repayment penalty** (unlike Lista) — you may repay any time before maturity at face value.

A borrower faces **two** liquidation triggers; both are cleared by repaying:

- **Before maturity — health (LTV) based.** As with any collateralised loan, if the collateral's oracle value falls enough to breach the market's LLTV, the position can be liquidated. Keep an LTV buffer, add collateral, or repay down.
- **At/after maturity — default.** The fixed `maturity` (surfaced on `params.market.maturity`) is a **hard deadline**. Once it passes, an unrepaid loan is **in default and can be liquidated regardless of health or LTV** — being past-due is itself the trigger. This is the Midnight-specific risk to watch: mark the maturity date and fully repay **before** it, even if the position is comfortably collateralised.

**Repay exactly the debt.** Over-repaying **reverts** on-chain (there is no over-repay buffer — do **not** pad the `amount`, and `isAll` does not auto-size here), while under-repaying leaves a dust position that stays open and therefore still liquidatable after maturity. Set `amount` to the position's current `debt` (loan-token units) from `/v1/data/lending/user-positions` and approve exactly that to the Midnight core. To **fully close and reclaim collateral**, repay the debt and then withdraw the collateral via [`/v1/actions/lending/withdraw`](https://docs.1delta.io/1delta-api/lending-withdraw/) — collateral cannot be freed while any debt remains.

**Teller markets (`TELLER_<pool>`):** pass `posId` = the `bidId` to repay (from the user-positions `term.loanId`). A **full** repay (`isAll`, or an `amount` ≥ the amount owed) uses `repayLoanFull` which repays principal + interest **and releases ALL the bid's collateral** — Teller has no keep-collateral partial close. A **partial** repay (`amount` < owed) uses `repayLoan` and keeps the collateral escrowed. ⚠ Liquidation is TIME-based and **aggressive**: after the term you have only a **short grace window** (`params.market.teller.paymentDefaultDuration`, as low as 5 min) to roll over or repay — miss it and the loan DEFAULTS, and a liquidator can seize your **ENTIRE collateral** (not just the amount owed). Repay or roll over **before** the deadline; there is no price buffer.

**Parameters**

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `marketUid` | query | string | yes | Market identifier (`lender:chainId:address`). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital). |
| `amount` | query | string | yes | Amount in wei |
| `mode` | query | "proxy" \| "direct" | no | Execution mode. proxy = 1delta composer, direct = raw protocol |
| `operator` | query | string | no | Wallet address of the user executing the action |
| `receiver` | query | string | no | Recipient of the lender position (deposit) or underlying tokens (borrow/withdraw). Defaults to `operator` when omitted. Custom-receiver support is per-lender. The API auto-selects the execution path: **Direct path supported** - Aave V2/V3 — `supply` / `withdraw` honor `to`. - Aave V4 — deposit via GiverPM `supplyOnBehalfOf`. Requires the GiverPM to be curated in `aave-v4-peripherals.json` with a name containing "giver". - Compound V3 — `supplyTo` / `withdrawTo`. - Morpho Blue & Lista DAO ERC-20 markets — Morpho `onBehalf`. - Euler V2 ERC-20 vaults — ERC-4626 `deposit(receiver)`. Collateral auto-enable is dropped for non-sub-account receivers. - Silo V2/V3 ERC-20 — `deposit(_, receiver, _)`. - Gearbox V3 passive-pool deposits — ERC-4626 `deposit(_, receiver)`. - Fluid borrow & withdraw — `operate(..., to_)` outflow slot. - Fluid **deposit to an existing NFT** when `accountId` is supplied — pure-deposit `operate()` calls bypass Fluid's owner auth gate, and the API folds a pre-flight `VaultFactory.ownerOf(nftId) == receiver` check into the merged multicall (rejects with 400 `INVALID_PARAM` on mismatch, `VALIDATION_FAILED` if the RPC can't confirm ownership). **Routes to composer (proxy) path** - Compound V2 family — direct `mint` is `msg.sender`-only. - Fluid **fresh-open deposits** — no `accountId` or `accountId=0`. The composer mints the new position NFT to itself, then transfers it to `receiver` via the encoder's `nftReceiver` slot. - Native-ETH vaults on Fluid (deposit / withdraw / repay) and the CompoundV2 family — the composer forwards `msg.value` to the vault's payable entrypoint. Other lenders' composer paths reject native asset and require wrapped-native as `payAsset`. **Direct-only branches that throw** - Gearbox V3 credit-side `addCollateral` — CA bound to operator. - Aave V4 native-gateway path — `supplyAsCollateralNative` has no recipient slot. **Unsupported in any single tx** - Init Capital — position NFT swept to operator. - Euler V2 / Silo V2/V3 native deposit paths — router/orchestrator credits msg.sender. - Fluid repay — on-chain primitive permits it, direct handler doesn't expose a receiver slot yet (routes to proxy as a conservative default). When the lender cannot honor a custom receiver on the direct path, `getTarget` falls back to `proxy` automatically. Forcing `mode=direct` on an unsupported path either throws (Gearbox credit-side, Aave V4 spoke, the explicit Silo router check) or silently credits the operator instead (Compound V2, Init, and Fluid fresh-open). |
| `isAll` | query | boolean | no | Withdraw/repay full balance. When on-chain position data is available, the API compares `amount` against the actual balance to decide the effective mode: - If `amount` covers the full position the action is treated as "withdraw/repay all" regardless of this flag. - If `isAll` is true but `amount` is less than the position, the API falls back to a partial action with the provided `amount` to avoid on-chain reverts. Without on-chain data, the flag is trusted as-is. |
| `lendingMode` | query | string | no | Interest rate mode (0=NONE, 1=STABLE, 2=VARIABLE) |
| `loanId` | query | string | no | **Lista DAO fixed-term (brokered) markets only.** Identifies which loan to repay. Pass the `loanId` of the target loan from the user's positions (each per-loan position in the user-positions response carries `loanId` and a `term` object). To repay the flexible / dynamic position instead, pass the sentinel `340282366920938463463374607431768211455` (`type(uint128).max`). The broker repays interest-first (plus an early-repayment penalty for not-yet-matured fixed loans) and refunds any excess to the caller, so to fully close a loan fund `outstanding + accruedInterest + earlyRepayPenalty` (all on the per-loan `term`). Required on brokered repays; ignored for non-brokered lenders. |
| `payAsset` | query | string | no | Asset to pay with. Use the zero address to pay with native ETH on lenders whose vault accepts native debt repayment (e.g. Init Capital, Fluid native-debt vaults, CompoundV2 `cETH` markets). Proxy mode: defaults to market asset. Native is forwarded as `msg.value` only when the lender supports it (e.g. Fluid, CompoundV2); otherwise the composer rejects. |
| `isShares` | query | boolean | no | Amount is in shares (direct mode) |
| `accountId` | query | string | no | Per-position identifier. Lender-specific: - **Init Capital** — account ID, required for borrow/withdraw/repay. - **Euler V2** — sub-account index (0..255), defaults to 0. - **Gearbox V3** — credit-account address (alias: `creditAccount`). - **Fluid** — position NFT id. Omit or pass `0` to mint a new position; supply an existing nftId to act on an existing position. Required for borrow/withdraw/repay; for deposit it unlocks the direct path with a custom `receiver` (deposit lands on the receiver-owned NFT after pre-flight `ownerOf` validation). |
| `slippage` | query | number | no | Slippage tolerance in **basis points** (`50` = 0.5%, `100` = 1%). Required whenever the action has to price something: - **Aggregator swap** — when `payAsset` / `receiveAsset` differs from the market's underlying, the currency conversion is routed through a DEX aggregator and this bounds that swap. - **Order-book take (Morpho Midnight)** — bounds the worst execution price accepted while filling offers. Ignored when neither applies (same-asset action on a pool lender). |
| `simulate` | query | boolean | no | When true, the API fetches the user's on-chain balances and returns projected post-trade metrics in the `simulation` field. Default: false. |

**Response `200`** — Transaction calldata and approvals for repay

| Field | Type | Description |
| --- | --- | --- |
| `success` | true |  |
| `data` | object | Informational data (quotes, simulation results, etc.) |
| `data.transaction` | object | An EVM transaction ready to sign and broadcast. Send `to`, `data` and `value` as-is; do not re-encode them. |
| `data.transaction.to` | string | Target contract address |
| `data.transaction.data` | string | Encoded calldata |
| `data.transaction.value` | string | ETH value to send with the transaction |
| `data.transaction.description` | string | Human-readable label. For `alternatives`, this is the aggregator name (e.g. "Paraswap"). For `transactions`, describes the setup action (e.g. "Switch e-mode to 1"). |
| `data.permissionTxns` | object[] | Approval transactions that must be executed before the main transaction. Empty when the user already has sufficient allowances. |
| `data.permissionTxns[].to` | string | Target contract address |
| `data.permissionTxns[].data` | string | Encoded calldata |
| `data.permissionTxns[].value` | string | ETH value |
| `data.permissionTxns[].description` | string | Human-readable description of the approval (e.g. "Approve borrow for AAVE_V3", "Approve ERC20") |
| `data.permissionTxns[].spender` | string | ERC-20 approve spender (x-chain permissions). Match it against the selected quote's `approvalTarget` — several bridges can share one spender, so do not match by description. |
| `data.rateImpact` | object[] | Projected interest-rate impact per market. Single-market actions produce 1 entry; loop actions produce 2. Null if IRM data is unavailable. |
| `data.rateImpact[].marketUid` | string | Market identifier (format: `lender:chainId:address`) |
| `data.rateImpact[].utilization` | object | A current/projected pair for a single rate metric. |
| `data.rateImpact[].utilization.current` | number | Current value |
| `data.rateImpact[].utilization.projected` | number | Projected value after the action |
| `data.rateImpact[].borrowRate` | object | A current/projected pair for a single rate metric. |
| `data.rateImpact[].borrowRate.current` | number | Current value |
| `data.rateImpact[].borrowRate.projected` | number | Projected value after the action |
| `data.rateImpact[].depositRate` | object | A current/projected pair for a single rate metric. |
| `data.rateImpact[].depositRate.current` | number | Current value |
| `data.rateImpact[].depositRate.projected` | number | Projected value after the action |
| `actions` | object | Transaction calldata and approvals. Null for quote-only responses (no account provided). |
| `actions.transactions` | object[] | Pre-trade setup transactions (e.g. e-mode switch, collateral enable). Execute these before the main swap. Empty when no setup is needed. |
| `actions.transactions[].to` | string | Target contract address |
| `actions.transactions[].data` | string | Encoded calldata |
| `actions.transactions[].value` | string | ETH value to send with the transaction |
| `actions.transactions[].description` | string | Human-readable label. For `alternatives`, this is the aggregator name (e.g. "Paraswap"). For `transactions`, describes the setup action (e.g. "Switch e-mode to 1"). |
| `actions.alternatives` | object[] | DEX aggregator swap transactions sorted by best output (descending). Each entry's `description` is the aggregator name. The client should pick one to execute. Present on loop action endpoints. |
| `actions.alternatives[].to` | string | Target contract address |
| `actions.alternatives[].data` | string | Encoded calldata |
| `actions.alternatives[].value` | string | ETH value to send with the transaction |
| `actions.alternatives[].description` | string | Human-readable label. For `alternatives`, this is the aggregator name (e.g. "Paraswap"). For `transactions`, describes the setup action (e.g. "Switch e-mode to 1"). |
| `actions.permissions` | object[] | Approval/delegation transactions that must execute before both `transactions` and `alternatives`. Includes ERC20 allowances (targeting the composer contract) and lender borrow/withdrawal delegations (targeting the lending protocol contract directly). Filtered against on-chain state so only missing approvals are returned. Null when no approvals are needed. |
| `actions.permissions[].to` | string | Target contract address |
| `actions.permissions[].data` | string | Encoded calldata |
| `actions.permissions[].value` | string | ETH value |
| `actions.permissions[].description` | string | Human-readable description of the approval (e.g. "Approve borrow for AAVE_V3", "Approve ERC20") |
| `actions.permissions[].spender` | string | ERC-20 approve spender (x-chain permissions). Match it against the selected quote's `approvalTarget` — several bridges can share one spender, so do not match by description. |

**Example response**

```json
{
  "success": true,
  "data": {
    "transaction": {
      "to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
      "data": "0x617ba037000000000000000000000000c02aaa39b2",
      "value": "0",
      "description": "string"
    },
    "permissionTxns": [
      {
        "to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
        "data": "0x617ba037000000000000000000000000c02aaa39b2",
        "value": "0",
        "description": "string",
        "spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
      }
    ],
    "rateImpact": [
      {
        "marketUid": "AAVE_V3:8453:0x4200000000000000000000000000000000000006",
        "utilization": {
          "current": 1,
          "projected": 1
        },
        "borrowRate": {
          "current": 1,
          "projected": 1
        },
        "depositRate": {
          "current": 1,
          "projected": 1
        }
      }
    ]
  },
  "actions": {
    "transactions": [
      {
        "to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
        "data": "0x617ba037000000000000000000000000c02aaa39b2",
        "value": "0",
        "description": "string"
      }
    ],
    "alternatives": [
      {
        "to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
        "data": "0x617ba037000000000000000000000000c02aaa39b2",
        "value": "0",
        "description": "string"
      }
    ],
    "permissions": [
      {
        "to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
        "data": "0x617ba037000000000000000000000000c02aaa39b2",
        "value": "0",
        "description": "string",
        "spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
      }
    ]
  }
}
```

**Response `400`** — Validation error

**Response `429`** — Rate limited. Unauthenticated callers share a per-IP budget; send an `x-api-key` header to lift it. Retry with exponential backoff.

**Response `500`** — Unexpected server error. Safe to retry with backoff.

**Response `502`** — An upstream data source or protocol origin failed (`error.code` is `ORIGIN_FAILED`). `error.details` carries the per-origin status. This is also what a missing or malformed required parameter currently returns, rather than a 400.
