> 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/plugin-integration.md).

# Plugin Integration

This guide walks you through integrating your project with Curvance's plugin architecture, allowing your application to interact with Curvance's smart contracts on behalf of users.

For an in-depth exploration of the Plugin and Delegation system architecture, see [Plugin & Delegation System](/app/developer-docs/plugin-and-delegation-system.md).

***

### Understanding Plugin Delegation

Curvance's plugin system enables third-party applications to perform actions on behalf of users through a per-contract delegation mechanism. This powers:

* Automated portfolio management.
* Advanced trading strategies.
* Cross-chain operations.
* Reward auto-compounding.
* Sequential action chaining.

#### Where delegation state lives

* **Each cToken** inherits `PluginDelegable` through `BaseCToken`. Calling `setDelegateApproval(delegate, true)` on a cToken authorizes that delegate only for delegation-gated methods on that cToken, such as `borrowFor`, `redeemFor`, `redeemCollateralFor`, `depositAsCollateralFor`, `postCollateralFor`, and `removeCollateralFor`.
* **Each PositionManager** inherits `PluginDelegable` through `BasePositionManager`. Calling `setDelegateApproval(delegate, true)` on that PositionManager authorizes `leverageFor` and `deleverageFor` on that PositionManager only.\
  \
  `repayFor` is different: it is not delegation-gated. The caller supplies the repayment assets and repays debt for `owner`.
* **The CentralRegistry**, through `ActionRegistry`, stores the user's global approval index and delegation-disable state. Incrementing the user's approval index with `incrementApprovalIndex()` invalidates existing per-contract delegation slots across Curvance contracts, because `isDelegate` reads the current approval index.

**Implication for integrators:** if your platform needs to act on multiple cTokens, or trigger both a PositionManager action and a cToken action, the user must delegate on each relevant `PluginDelegable` contract separately.

***

### Enabling Delegation with `setDelegateApproval()`

```js
import { ethers } from "ethers";

const POSITION_MANAGER_ADDRESS = "0x...";
const PLATFORM_DELEGATE_ADDRESS = "0x...";

const PLUGIN_DELEGABLE_ABI = [
  "function setDelegateApproval(address delegate,bool isApproved)",
  "function checkNewDelegationDisabled(address user) view returns (bool)",
  "error PluginDelegable__DelegatingDisabled()",
  "error PluginDelegable_InvalidParameter()"
];

const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const userAddress = await signer.getAddress();

const positionManager = new ethers.Contract(
  POSITION_MANAGER_ADDRESS,
  PLUGIN_DELEGABLE_ABI,
  signer
);

async function approveDelegate() {
  if (await positionManager.checkNewDelegationDisabled(userAddress)) {
    throw new Error("Delegation changes are disabled or still in cooldown.");
  }

  const tx = await positionManager.setDelegateApproval(
    PLATFORM_DELEGATE_ADDRESS,
    true
  );

  const receipt = await tx.wait();
  return receipt;
}
```

**Call `setDelegateApproval` on every contract your platform needs to control:**

* The PositionManager for `leverageFor` and `deleverageFor`.
* Each cToken for delegation-gated cToken actions: `borrowFor`, `redeemFor`, `redeemCollateralFor`, `depositAsCollateralFor`, `postCollateralFor`, and `removeCollateralFor`.

A single approval is not global. `repayFor` is intentionally outside this list: any caller can repay debt for `owner` with assets the caller supplies, subject to the repay path's own market checks.

**Blocked edge cases:**

* `setDelegateApproval(msg.sender, _)` reverts with `PluginDelegable_InvalidParameter()`. A user cannot delegate to themselves.
* `setDelegateApproval(delegate, isApproved)` checks `checkNewDelegationDisabled(msg.sender)` for both approvals and per-contract revocations. That view returns true when delegation is disabled or when the re-enable cooldown has not expired. If it returns true, `setDelegateApproval` reverts with `PluginDelegable__DelegatingDisabled()`.

#### Error handling (ethers v6 pattern)

```js
async function approveDelegateSafe() {
  try {
    return await approveDelegate();
  } catch (error) {
    const errData = error.data ?? error.info?.error?.data;
    if (errData) {
      try {
        const decoded = positionManager.interface.parseError(errData);
        if (decoded.name === "PluginDelegable__DelegatingDisabled") {
          console.error("Delegation is disabled for this account");
        } else if (decoded.name === "PluginDelegable_InvalidParameter") {
          console.error("Cannot delegate to yourself");
        } else {
          console.error(`Revert: ${decoded.name}`, decoded.args);
        }
      } catch (_) {
        console.error("Unrecognized revert data:", errData);
      }
    } else {
      console.error("Non-revert error:", error.message);
    }
    throw error;
  }
}
```

***

### Verifying Delegation Status with `isDelegate()`

```js
const { ethers } = require("ethers");

const { YourProject_Address }            = require("./config/addresses");
const { CToken_PositionManager_Address } = require("./config/addresses");
const { CToken_PositionManager_ABI }     = require("./abis/positionManagerAbi");

// isDelegate is a view function; a provider is sufficient (no signer needed)
const provider        = new ethers.JsonRpcProvider("YOUR_RPC_URL");
const positionManager = new ethers.Contract(
  CToken_PositionManager_Address,
  CToken_PositionManager_ABI,
  provider
);

async function checkDelegate(userAddress) {
  // isDelegate(user, delegate) - both are addresses
  const approved = await positionManager.isDelegate(
    userAddress,
    YourProject_Address
  );
  console.log(`Platform is an approved delegate of ${userAddress}: ${approved}`);
  return approved;
}
```

{% hint style="warning" %}
**Important:** `isDelegate` reads `_isDelegate[user][userApprovalIndex(user)][delegate]`. If the user has mass-revoked (via `incrementApprovalIndex` on the CentralRegistry), **every** prior delegation immediately returns `false` without changing any per-contract state. Your integration should treat `isDelegate` as the source of truth before attempting a delegated action.
{% endhint %}

***

### Revoking Delegation

Users have two revocation paths.

#### Per-contract revocation

Call `setDelegateApproval(platformAddress, false)` on the specific cToken or PositionManager. This affects only that contract's delegation mapping at the user's current approval index. Because it uses `setDelegateApproval`, it still reverts while delegation changes are disabled or still in cooldown.

```js
const tx = await positionManager.setDelegateApproval(
  PLATFORM_DELEGATE_ADDRESS,
  false
);
await tx.wait();
```

#### Mass revocation (all contracts at once)

Call `incrementApprovalIndex()` on the CentralRegistry. This increments the caller's approval index, which invalidates previous delegation mappings across Curvance contracts without touching each cToken or PositionManager.

```js
const CENTRAL_REGISTRY_ADDRESS = "0x...";
const CENTRAL_REGISTRY_ABI = [
  "function incrementApprovalIndex()"
];

const centralRegistry = new ethers.Contract(
  CENTRAL_REGISTRY_ADDRESS,
  CENTRAL_REGISTRY_ABI,
  signer
);

const tx = await centralRegistry.incrementApprovalIndex();
await tx.wait();
```

Surface mass revocation prominently, especially in UIs that list many delegated platforms.

***

### Verification and Security Best Practices

* **Verify delegation before every delegation-gated action.** Call `isDelegate(user, platform)` on the specific cToken or PositionManager before calling a delegation-gated `*For` method. `repayFor` is not delegation-gated, but still runs the repay path's market checks.
* **Do not rely only on per-contract event caches.** Mass revocation emits `ApprovalIndexIncremented` from the CentralRegistry, not `DelegateApproval` from each cToken or PositionManager, so a cache built only from per-contract events can go stale.
* **Surface revocation options clearly.** Users should be able to revoke a specific platform with `setDelegateApproval(platform, false)` when delegation changes are enabled, or revoke everything with `incrementApprovalIndex()`.
* **Minimize scope.** Only request delegation on the contracts you actually need. Delegation on one cToken does not imply delegation on another cToken or on a PositionManager.
* **Transparent permissions.** Clearly explain which contracts the user is authorizing and which delegation-gated actions your platform will perform.
* **Watch for `checkNewDelegationDisabled`.** If it returns true, `setDelegateApproval` reverts for approvals and per-contract revocations. Detect this upfront with `checkNewDelegationDisabled(userAddress)` and provide a helpful error.
* **Use PositionManagers for bundled leverage flows.** `leverageFor` and `deleverageFor` already perform the borrow-swap-deposit and redeem-swap-repay loops through the PositionManager flow.

***

### Next Steps

After implementing the delegation setup, your application should:

1. Verify each delegation was successful using `isDelegate` on every relevant contract.
2. Store the user's delegation status in your application state, **but re-verify before every delegated action** (mass-revocation is silent).
3. Implement the specific delegation-gated actions your application needs: `borrowFor`, `redeemFor`, `redeemCollateralFor`, `leverageFor`, `deleverageFor`, `depositAsCollateralFor`, `postCollateralFor`, and `removeCollateralFor`. Treat `repayFor` as a non-delegated repay path where the caller supplies assets to repay debt for `owner`.
4. Provide users with both per-contract and global revocation flows.

For the full list of delegable actions and their signatures, see List of Delegable Actions.

For technical support or to get your plugin featured in Curvance's ecosystem, contact the developer relations team.

***


---

# 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/plugin-integration.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.
