> For the complete documentation index, see [llms.txt](https://docs.curvance.com/app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.curvance.com/app/developer-docs/quick-start-guides/integration-cookbook.md).

# Integration Cookbook

## Overview

This page provides walkthroughs for various integration scenarios to suit your needs.

1. [Parking Your Users' Idle Funds in Curvance](/app/developer-docs/quick-start-guides/integration-cookbook.md#parking-your-users-idle-funds-in-curvance)
2. [Include a Curvance Market in Your Vault](/app/developer-docs/quick-start-guides/integration-cookbook.md#include-a-curvance-market-in-your-vault)
3. More coming soon

{% hint style="danger" %}
To minimize the risk of bad debt and potential loss of funds, deposit user funds only into conservative isolated markets with low-risk profiles. If the goal is lender yield, also confirm `await marketManager.debtCaps(cToken) > 0n`; `0n` means the asset can be deposited but cannot be borrowed in that market.
{% endhint %}

***

### Parking Your Users' Idle Funds in Curvance

**Objectives:**

* Safely park user idle funds in Curvance markets.

#### One time setup (per cToken market)

The user calls [`cUSDC.setDelegateApproval(<yourPlatformAddress>, true)`](/app/developer-docs/plugin-and-delegation-system/delegable-actions.md#setdelegateapproval), delegating your platform's address as the delegate.

**From your platform**, call `approve` on the underlying asset (e.g., USDC) so the cToken can pull funds during `deposit`.&#x20;

{% hint style="info" %}
*Tip: approve a large amount once to avoid a per-deposit approval transaction.*
{% endhint %}

{% hint style="info" %}
Note: Users can mass-revoke all delegates anytime by calling `incrementApprovalIndex()` on the Central Registry.
{% endhint %}

#### **Depositing idle funds**

**From your platform:** source USDC (e.g., transferred to you by the user) and call [`cUSDC.deposit(assets, <userAddress>)`](/app/developer-docs/lending-protocol/ctoken.md#deposit).

{% hint style="info" %}
Note: deposits pull USDC from the caller (msg.sender), not the `receiver`.
{% endhint %}

#### Retrieving funds on demand

Call [`cUSDC.redeemFor(shares, <yourPlatformAddress>, <userAddress>)`](/app/developer-docs/lending-protocol/ctoken.md#redeemfor) to redeem the user's cUSDC shares and receive USDC.

{% hint style="info" %}
`redeemFor` uses the delegation system, so **no ERC20 share approval is required**; your platform just needs to be an approved delegate of the user.
{% endhint %}

{% hint style="info" %}
**Note:** there is no `withdrawFor`; withdrawals on behalf of a user must go through `redeemFor` (in shares). If the user wants to pull a specific amount of underlying, compute shares with `previewWithdraw(assets)` first, then call `redeemFor(shares, platform, user)`.
{% endhint %}

### Collateralizing on Behalf of Users

*If your integration wants user funds held **as collateral** (not just earning yield), plain* `deposit` *won't credit them as collateral.*

* The user must first delegate your platform via `setDelegateApproval`.
* Your platform then calls `cToken.depositAsCollateralFor(assets, <userAddress>)`; this deposits *and* posts the shares as collateral in one call.
* Alternative: call `postCollateralFor(shares, <userAddress>)` to post existing shares as collateral.
* Plain `depositAsCollateral` is restricted to `msg.sender == receiver` or the PositionManager, so non-self collateralization requires the `*For` variants + delegation.

{% hint style="info" %}
**Note:** posting collateral or borrowing starts an account-level 20-minute cooldown. During the cooldown, deposits still work, but repayment and cToken transfer, redeem, and withdraw paths that call the MarketManager transfer check revert. If your UX offers instant exit or repayment, gate it on the cooldown expiry or surface a wait time to the user.
{% endhint %}

***

### Include a Curvance Market in Your Vault

**Objectives:**

* Mint cTokens to your vault.
* Redeem cTokens to rebalance/withdraw

#### Mint cTokens when users mint vault shares

1. Approve cToken to spend the underlying ERC20 from your vault.&#x20;
2. Call [`cToken.deposit()`](/app/developer-docs/lending-protocol/ctoken.md#deposit) from your contract, with its address as the recipient.

#### Redeem cTokens to rebalance (if applicable) / or withdraw user funds

Call [`cToken.redeem()`](/app/developer-docs/lending-protocol/ctoken.md#redeem) if redeeming in shares, or [`cToken.withdraw()`](/app/developer-docs/lending-protocol/ctoken.md#withdraw) if redeeming in underlying.

{% hint style="info" %}
No ERC20 share approval is required **because your vault is both the caller and the owner of the shares** (standard ERC4626 behavior: allowance is only checked when `msg.sender != owner`).
{% endhint %}

***

### Stale vs Fresh Reads

Curvance cTokens accrue interest lazily: the exchange rate updates only when someone calls a state-changing function (`deposit`, `withdraw`, `redeem`, `borrow`, `repay`, liquidation). Between those on-chain touches, the real rate drifts upward (for lenders) as time passes, but **`view` functions cannot accrue state and therefore return slightly stale values**.

This matters for any integration that displays balances, computes liquidation margins, or tries to hit exact target amounts.

| Surface                                   | Acceptable read                                  | Why                                                                                       |
| ----------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| UX hint, "you'd receive \~X"              | `previewRedeem`, `previewWithdraw` (stale)       | Drift is sub-bp; preview rounding matches the eventual tx                                 |
| Health factor, liquidation margin         | `getSnapshotUpdated.staticCall(account)` (fresh) | Stale values can mask imminent liquidation                                                |
| "Redeem all" share count                  | `balanceOf(user)` (stale)                        | The on-chain `redeem` accrues before payout, so user receives the freshly-earned interest |
| Backend accounting, "send exactly N USDC" | `exchangeRateUpdated.staticCall()` (fresh)       | Wei-level accuracy required                                                               |
| Collateral cap UI                         | `marketCollateralPosted` (stale)                 | Share-count invariant, not rate-sensitive                                                 |

#### What is and isn't stale

<table><thead><tr><th>Function</th><th width="239">Accrues first?</th></tr></thead><tbody><tr><td><code>convertToAssets</code> / <code>convertToShares</code></td><td>No, stale</td></tr><tr><td><code>previewDeposit</code> / <code>previewMint</code> / <code>previewWithdraw</code> / <code>previewRedeem</code></td><td>No, stale</td></tr><tr><td><code>totalAssets</code> / <code>exchangeRate</code> / <code>maxWithdraw</code> / <code>maxRedeem</code></td><td>No, stale</td></tr><tr><td><code>getSnapshot(account)</code></td><td>No, stale</td></tr><tr><td><code>exchangeRateUpdated()</code></td><td>Yes, fresh</td></tr><tr><td><code>getSnapshotUpdated(account)</code></td><td>Yes, fresh</td></tr><tr><td>Actual <code>deposit</code> / <code>withdraw</code> / <code>redeem</code> / <code>borrow</code> / <code>repay</code></td><td>Yes, fresh (accrues before computing)</td></tr></tbody></table>

#### Practical implications

1. **Display staleness is in the user's favor on redemption.** A stale `previewRedeem` under-reports what the user will actually receive. The real `redeem` accrues first, using the fresher (higher) rate, so the user ends up with slightly more than previewed.
2. **Watch direction when converting from a target asset amount.** If a user enters "1000 USDC" and you compute shares via `previewWithdraw(1000)` then call `redeem(shares, ...)` or `redeemFor(shares, ...)`, the user can receive slightly more than 1000 USDC because the on-chain rate is fresher. If the caller is also the share owner, or has ERC20 share allowance from the owner, use `withdraw(1000, receiver, owner)` to target an exact asset amount. If your platform only has Curvance delegation, there is no cToken `withdrawFor`; use `previewWithdraw(assets)` plus `redeemFor(shares, receiver, owner)` and treat the output as at least the target, subject to state changes, liquidity, pauses, and account checks.
3. "Redeem all" is safe from a staleness perspective. Pass `balanceOf(userAddress)` into `redeem(shares, ...)` and the on-chain accrual pulls freshly-earned interest into the payout. The transaction can still revert due to cooldown, redemption pause, insufficient held assets, or collateral and health checks.
4. **Liquidation margin and health factor must use fresh reads.** If your UX shows "you can borrow X more" or "liquidation at Y", computing that from stale `getSnapshot` can display a user as healthier than they actually are, masking imminent liquidation. Always use `getSnapshotUpdated` via `staticCall` for these surfaces.
5. **Collateral cap display can safely use stale reads.** `marketCollateralPosted` is a share-count invariant, not a rate-sensitive value; staleness doesn't apply.

#### Fresh reads via `staticCall` (no gas, no state change)

`exchangeRateUpdated()` and `getSnapshotUpdated(account)` are **not** `view`; they call `_accrueIfNeeded()` before reading. A normal call would cost gas, but `staticCall` simulates the transaction and returns the accrued result without committing any state. This is the idiomatic pattern for "fresh reads":

```js
// Freshly-accrued exchange rate (WAD-scaled, shares-to-assets)
const freshRate = await cUSDC.exchangeRateUpdated.staticCall();

// Freshly-accrued AccountSnapshot: 
// { asset, underlying, decimals, isCollateral, collateralPosted, debtBalance }
const snap = await cUSDC.getSnapshotUpdated.staticCall(userAddress);
```

**When to use which:**

* **`convertTo*` / `preview*`**: cheap, stale. Fine for UX hints, rounding behavior that matches the actual tx, and sanity-checks that a value is within range. Use for low-stakes surfaces that update frequently anyway.
* **`exchangeRateUpdated.staticCall()` / `getSnapshotUpdated.staticCall()`**: fresh, slightly more RPC overhead than a pure view call but no gas. Use whenever wei-accuracy matters: health factors, liquidation-margin displays, "you will receive at least X" guarantees, and any backend accounting.
* **Actual `withdraw` / `redeem` / `borrow` / `repay`**: always use the fresh rate on-chain; you don't need to pre-accrue, and there's no way for your tx to use a stale rate.

**Forcing accrual on-chain.** If your contract needs `exchangeRate()`, `getSnapshot()`, or `convertTo*` to be accrued before a same-tx downstream read, call `cToken.accrueIfNeeded()` first. It runs `_accrueIfNeeded()` and writes the fresh state to storage so subsequent stale-view reads in the same tx return current values.

#### Stale estimates (illustrative, fine for UX hints)

```js
const userAddress = await signer.getAddress();

// cUSDC is a per-market cToken address. Source the correct address
// from the deployment registry for the chain and market you are integrating.
const cUSDC = new ethers.Contract(
  ADDRESSES.USDC_CTOKEN,
  CTOKEN_ABI,
  signer
);

// Amount of USDC for a given share count (stale)
const shares = await cUSDC.balanceOf(userAddress);
const assetsOut = await cUSDC.convertToAssets(shares);

// Shares for a given USDC amount (stale)
const assetsIn      = ethers.parseUnits("1000", 6);
const sharesApprox  = await cUSDC.convertToShares(assetsIn);  // rounds DOWN
const sharesExact   = await cUSDC.previewWithdraw(assetsIn);  // rounds UP, matches the actual withdraw call
```

***

#### Practical Tips

* Use previews (`previewDeposit()`, `previewWithdraw()`, `previewMint()`, `previewRedeem()`) client-side for UX estimates. `maxDeposit()` and `maxMint()` return `0` only when the cToken is unlisted or specifically mint-paused; they do not surface deposit caps or other pause flags. For full pause coverage, call `marketManager.actionsPaused(cToken)` and inspect all three flags `(mintPaused, collateralizationPaused, borrowingPaused)`.
* Pauses/caps may limit deposits/redemptions; handle errors gracefully.
* **Reentrancy:** all deposit/redeem/collateral entrypoints are `nonReentrant` (transient-storage lock). If your integration calls into cTokens from a callback, it will revert.
* **Security:** delegation lets your platform act for users; encourage users to delegate only trusted contracts and remind them they can revoke instantly via approval index.
* **Self-delegation is blocked:** `setDelegateApproval(msg.sender, ...)` reverts; users can't set themselves as their own delegate, so your platform must be a distinct address.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.curvance.com/app/developer-docs/quick-start-guides/integration-cookbook.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
