Skip to main content

Simulate a base lending operation on-chain

GET 

/v1/actions/lending/simulate

Build a base lending operation and execute it against live chain state before the user signs it.

This is an EVM check, not the arithmetic projection that simulate=true adds to the other lending endpoints. The transaction is built by the very endpoint the client would call (/v1/actions/lending/{action}) and then replayed through an eth_call with storage overrides — so what is verified is the exact calldata that would be submitted, including the lender quirks that only surface on-chain.

Scope is the four single-grant operations. deposit and repay override the pay token (funding balance and approval in one write); withdraw and borrow override the lender's own grant mapping (Aave credit delegation, Morpho isAuthorized, Comet isAllowed) so the action can be previewed before the user has signed that permission. Anything needing two grants at once — notably a leverage open that pulls fresh margin and borrows on behalf — cannot be previewed cold and is out of scope.

The override is optimistic. willSucceed: true with grantOverridden: true means "this succeeds once the permission exists"; it is not evidence the permission exists. Keep gating the real grant on the permissions array.

The simulation object is either:

  • { simulated: true, willSucceed, gasUsed, amountOut, returnData, revertReason?, block, grantOverridden }
  • { simulated: false, reason, detail } — where reason is one of NO_TRANSACTION, MULTI_STEP, NATIVE_VALUE, MULTIPLE_GRANTS, GRANT_SLOT_UNKNOWN, SIMULATOR_UNAVAILABLE.

Note amountOut is a measured balance delta on operator, so it reads 0 for lenders with no receipt token (Morpho Blue and every CDP-shaped lender) — those return their own values in returnData instead. A simulator outage never fails the request: the calldata is still returned and remains valid.

Plain-text reference — GET /v1/actions/lending/simulate

Parameters

ParameterInTypeRequiredDescription
actionquerystringyesWhich base operation to build and replay: deposit, withdraw, borrow or repay.
marketUidquerystringyesMarket identifier (lender:chainId:address). The address encodes the underlying token (Aave/Morpho/CompoundV3), cToken (CompoundV2), or pool (Init Capital).
amountquerystringyesAmount in wei
modequeryproxy, directnoExecution mode. proxy = 1delta composer, direct = raw protocol
operatorquerystringnoWallet address of the user executing the action
receiverquerystringnoRecipient 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). | | payAsset | query | string | no | Asset to pay with.

Use the zero address to pay with native ETH on lenders whose vault accepts native (e.g. Init Capital, Fluid native-ETH 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. | | block | query | string | no | Block number or tag to simulate against. Defaults to latest. |

Request

Responses

Transaction calldata plus the on-chain execution result