Saltar a contenido

Curvance

Connector for the Curvance isolated-market lending protocol. Curvance pairs a collateral cToken (ERC-4626-style) with a BorrowableCToken (debt side) per market. Each market is governed by its own MarketManager — there is no global pool.

Supported chains: Monad.

Supported markets on Monad (as of 2026-04-18): ezETH→WETH, WETH→USDC, WMON→USDC, WBTC→USDC, aprMON→WMON, shMON→WMON. See [CURVANCE_MARKETS][almanak.framework.connectors.curvance.CURVANCE_MARKETS] for the authoritative list.

Quick example

from decimal import Decimal
from almanak.framework.intents import SupplyIntent

intent = SupplyIntent(
    protocol="curvance",
    token="WMON",
    amount=Decimal("1.0"),
    use_as_collateral=True,
    market_id="0xa6A2A92F126b79Ee0804845ee6B52899b4491093",  # WMON-USDC MarketManager
    chain="monad",
)

Known constraints

  • 20-minute MIN_HOLD_PERIOD on collateral before it can be withdrawn (reverts with MarketManager__MinimumHoldPeriod()).
  • MIN_LOAN_SIZE per market, set at deployment in the [10e18, 100e18] WAD range. Borrows below the market's floor revert with LiquidityManager__InsufficientLoanSize().
  • redeemCollateral does not accept MAX_UINT256 as a "redeem all" sentinel — strategies must read balanceOf(user) on the cToken and pass the exact share count. Use withdrawCollateral(assets, receiver, owner) for asset-amount-denominated exits.
  • repay(0) is Curvance's full-debt-repay sentinel (NOT MAX_UINT256).

Module reference

almanak.framework.connectors.curvance

Curvance protocol connector.

Curvance is a permissionless isolated-market lending protocol deployed on Monad. Unlike Compound/Aave-style protocols with a single pool, Curvance deploys a dedicated cToken (collateral, ERC-4626-style) and BorrowableCToken (debt side) pair per market. The market_id used throughout the adapter is the MarketManager address.

Key entry points

CurvanceAdapter — high-level interface (supply_collateral, borrow, repay, withdraw) CurvanceConfig — adapter configuration (chain, wallet, optional gateway client) CurvanceSDK — low-level calldata/encoding helpers CurvanceReceiptParser — event parsing for ResultEnricher CURVANCE_MARKETS — per-chain market registry (MarketManager -> cToken / BorrowableCToken)

Supported chains: Monad.

CurvanceAdapter

CurvanceAdapter(
    config: CurvanceConfig,
    token_resolver: TokenResolver | None = None,
)

Adapter for Curvance lending protocol (Monad).

Produces EVM calldata for supply/borrow/repay/withdraw against Curvance isolated markets. The adapter is stateless between calls — each operation returns a standalone TransactionResult.

Example

config = CurvanceConfig(chain="monad", wallet_address="0x...") adapter = CurvanceAdapter(config) market_id = "0xb3E9E0134354cc91b7FB9F9d6C3ab0dE7854BB49" # WETH-USDC result = adapter.supply_collateral(market_id=market_id, amount=Decimal("0.001")) result.tx_data["to"] # -> WETH cToken address result.tx_data["data"] # -> depositAsCollateral calldata

markets property

markets: dict[str, CurvanceMarket]

All registered markets on this chain (keyed by MarketManager address, lowercase).

get_market

get_market(market_id: str | None = None) -> CurvanceMarket

Resolve a market by MarketManager address; uses chain default if omitted.

supply_collateral

supply_collateral(
    market_id: str,
    amount: Decimal,
    on_behalf_of: str | None = None,
) -> TransactionResult

Build a depositAsCollateral transaction.

Deposits the underlying asset into the market's collateral cToken and atomically posts the resulting shares as collateral. No separate enterMarkets call is required.

Parameters:

Name Type Description Default
market_id str

MarketManager address (case-insensitive).

required
amount Decimal

Amount of underlying asset (human units).

required
on_behalf_of str | None

Receiver of the cToken shares. Defaults to wallet_address.

None

Returns:

Type Description
TransactionResult

TransactionResult with tx_data targeting the collateral cToken.

borrow

borrow(
    market_id: str,
    amount: Decimal,
    receiver: str | None = None,
) -> TransactionResult

Build a borrow transaction on the market's BorrowableCToken.

Parameters:

Name Type Description Default
market_id str

MarketManager address (case-insensitive).

required
amount Decimal

Amount of debt asset to borrow (human units).

required
receiver str | None

Address that receives the borrowed assets. Defaults to wallet_address. The debt is always owned by msg.sender (the wallet); only the destination of the proceeds can be redirected.

None

Returns:

Type Description
TransactionResult

TransactionResult with tx_data targeting the borrowable cToken.

repay

repay(
    market_id: str,
    amount: Decimal,
    repay_full: bool = False,
) -> TransactionResult

Build a repay transaction on the market's BorrowableCToken.

When repay_full is True, the adapter encodes repay(0) — Curvance treats 0 as "repay the caller's entire outstanding debt" (verified in BorrowableCToken.repay NatSpec and _repay logic). MAX_UINT256 is NOT a repay sentinel here; that would be treated as an over-repayment and revert.

Caller balance requirement: repay(0) calls debtBalance(msg.sender) at execution time and does transferFrom(msg.sender, market, that_amount). The caller must hold the full debt (principal + accrued interest) in the debt-underlying token at submission time, or transferFrom reverts. Mainnet-verified 2026-04-18 on Monad cUSDC 0x8EE9FC...774: TX 0x176a941e…687cdb pulled 11,000,025 USDC to clear an 11,000,013 principal borrow after ~20 min of accrued interest. Strategies should keep ≳0.01% slack over the last-read debtBalance when using this path.

Parameters:

Name Type Description Default
market_id str

MarketManager address.

required
amount Decimal

Amount of debt asset to repay (human units). Ignored when repay_full=True.

required
repay_full bool

If True, repay the entire outstanding debt using the 0-sentinel.

False

Returns:

Type Description
TransactionResult

TransactionResult with tx_data targeting the borrowable cToken.

withdraw_collateral

withdraw_collateral(
    market_id: str,
    amount: Decimal,
    withdraw_all: bool = False,
    receiver: str | None = None,
    share_balance: int | None = None,
) -> TransactionResult

Build a collateral-withdraw transaction.

Two paths are supported:

- **Asset-amount** (default) → ``withdrawCollateral(assets, receiver, owner)``
  on the collateral cToken. Forces collateral to unwind and sends
  ``assets`` of the underlying to ``receiver``.
- **Share-amount** via ``withdraw_all=True`` + ``share_balance``
  → ``redeemCollateral(shares, receiver, owner)`` on the cToken.
  Used for exact full-exit when the strategy has read ``balanceOf``
  from the collateral cToken and wants to burn every share cleanly.

Parameters:

Name Type Description Default
market_id str

MarketManager address.

required
amount Decimal

Amount of underlying asset to withdraw (human units). Ignored when withdraw_all=True.

required
withdraw_all bool

Redeem full share balance. Requires share_balance.

False
receiver str | None

Recipient of the underlying. Defaults to wallet_address.

None
share_balance int | None

Exact cToken share balance to burn. Required when withdraw_all=True. Curvance's redeemCollateral does NOT treat MAX_UINT256 as a clamping sentinel — callers MUST read the share balance themselves.

None

Returns:

Type Description
TransactionResult

TransactionResult with tx_data targeting the collateral cToken.

Note

Curvance enforces a 20-minute MIN_HOLD_PERIOD on collateral before it can be withdrawn. Earlier calls revert with MarketManager__MinimumHoldPeriod().

get_supply_spender

get_supply_spender(market_id: str) -> str

Return the contract that must be approved before supply_collateral.

For Curvance this is the collateral cToken — it calls transferFrom(user, self, assets) inside depositAsCollateral.

get_repay_spender

get_repay_spender(market_id: str) -> str

Return the contract that must be approved before repay.

For Curvance this is the BorrowableCToken — it calls transferFrom(payer, self, assets) inside repay.

CurvanceConfig dataclass

CurvanceConfig(
    chain: str,
    wallet_address: str,
    gateway_client: GatewayClient | None = None,
)

Configuration for CurvanceAdapter.

Attributes:

Name Type Description
chain str

Target chain (currently only "monad").

wallet_address str

User wallet address (0x-prefixed EVM address).

gateway_client GatewayClient | None

Optional gateway client for on-chain reads. Reserved for future use (pause checks, debt balance queries). Adapter builds calldata offline so the gateway is not required for transaction construction.

CurvanceMarketInfo dataclass

CurvanceMarketInfo(
    name: str,
    market_manager: str,
    collateral_ctoken: str,
    borrowable_ctoken: str,
    collateral_symbol: str,
    debt_symbol: str,
)

Serializable snapshot of a Curvance market for logging / metadata.

CurvanceReceiptParser

Parse Curvance transaction receipts into structured events.

The parser matches the minimal interface expected by ResultEnricher:

parser.parse_receipt(receipt) -> CurvanceReceiptResult
parser.extract_supply_amount(receipt) -> int | None
parser.extract_borrow_amount(receipt) -> int | None
parser.extract_withdraw_amount(receipt) -> int | None
parser.extract_repay_amount(receipt) -> int | None

parse_receipt

parse_receipt(
    receipt: dict[str, Any],
) -> CurvanceReceiptResult

Parse a web3 tx receipt into a structured Curvance result.

extract_supply_amount

extract_supply_amount(
    receipt: dict[str, Any],
) -> int | None

Return the first Deposit event's assets amount (wei). None if absent.

extract_withdraw_amount

extract_withdraw_amount(
    receipt: dict[str, Any],
) -> int | None

Return the first Withdraw event's assets amount (wei).

extract_borrow_amount

extract_borrow_amount(
    receipt: dict[str, Any],
) -> int | None

Return the first Borrow event's assets amount (wei).

extract_repay_amount

extract_repay_amount(receipt: dict[str, Any]) -> int | None

Return the first Repay event's assets amount (wei).

extract_supply_amount_decimal

extract_supply_amount_decimal(
    receipt: dict[str, Any], decimals: int
) -> Decimal | None

Human-units variant of extract_supply_amount.

CurvanceSDK

CurvanceSDK(chain: str)

Thin stateless wrapper exposing calldata/encoder helpers.

The SDK is stateless by design — higher-level state (wallet address, gateway client, chain-scoped market lookup) belongs in the adapter. A class form is kept for parity with other connectors (e.g. MorphoBlueSDK) and to make future extension easier without changing import sites.