import { type Address, type Hex, type StateOverride } from "viem"; import { Account } from "../../entities/Account.js"; import { type DecodedSmartContractError } from "../../utils/decodeSmartContractErrors.js"; import type { SlotHints } from "../../utils/stateOverrides/slotHints.js"; import type { AccountFetchOptions } from "../accountService/accountService.js"; import type { IABIService } from "../abiService/index.js"; import type { IDeploymentService } from "../deploymentService/index.js"; import type { IEulerLabelsService } from "../eulerLabelsService/index.js"; import type { IIntrinsicApyService } from "../intrinsicApyService/index.js"; import type { IPriceService } from "../priceService/index.js"; import type { ProviderService } from "../providerService/index.js"; import type { IRewardsService } from "../rewardsService/index.js"; import type { VaultFetchOptions } from "../vaults/index.js"; import type { IVaultMetaService, VaultEntity } from "../vaults/vaultMetaService/index.js"; import type { IWalletService } from "../walletService/index.js"; import type { BatchItemDescription, EVCBatchItem, TransactionPlan } from "./executionServiceTypes.js"; type BatchItemResult = { success: boolean; result: Hex; }; export type SimulationInsufficientRequirement = { token: Address; amount: bigint; }; /** * An AccountLens read that produced no position, leaving a snapshot layer * incomplete. * * These never appear in `failedBatchItems`: `rawBatchResults` covers only the * action positions of the batch, so lens reads are outside it, and a whole-vault * failure is reported in-band by the lens with the batch item itself succeeding. */ export type SimulationSnapshotReadFailure = { /** * Index into `simulatedAccounts`: 0 = pre-batch (real) state, i = state after * operation i. The highest index is the final post-batch state. */ layerIndex: number; /** Sub-account whose decoded position set is incomplete. */ subAccount: Address; /** Vault the lens could not report on. Absent for account-scoped reads. */ vault?: Address; /** Which lens read failed. */ kind: "vaultAccount" | "evcAccount"; /** * `inBand`: the read succeeded and the lens set `queryFailure`. * `revert`: the lens read itself reverted. */ cause: "inBand" | "revert"; /** The lens `queryFailureReason`, or the reverted read's return data. */ reason?: Hex; }; export interface SimulateBatchResult { /** * Per-layer simulated account snapshots: index 0 = pre-batch (real) state, * index i = state after operation i. The last entry is the final state. */ simulatedAccounts: Account[]; /** Final-layer vault snapshots (the last entry of `simulatedVaultsLayers`). */ simulatedVaults: TVaultEntity[]; /** Per-layer vault snapshots aligned with `simulatedAccounts`. */ simulatedVaultsLayers?: TVaultEntity[][]; /** * Per-layer wallet ERC20 balances (lowercased token address → balance) for the * underlying assets of touched vaults, aligned with `simulatedAccounts`. * Balances are forged by state overrides, so consumers should stitch using the * delta vs layer 0. */ simulatedWalletBalances?: Record[]; /** * Whether the batch itself is expected to execute. A failed AccountLens read * does not stop the batch, so this stays `true` when one occurs — check * `snapshotReadFailures` before treating `simulatedAccounts` as a complete * post-state (e.g. before deriving a health factor from it). */ canExecute: boolean; /** * Lens reads that yielded no position, so the corresponding layer of * `simulatedAccounts` is missing a collateral or debt position it may * actually hold. Absent when every lens read reported cleanly. */ snapshotReadFailures?: SimulationSnapshotReadFailure[]; rawBatchResults?: BatchItemResult[]; failedBatchItems?: Array<{ index: number; /** Index of the operation (cart entry) this batch item belongs to. */ operationIndex?: number; /** Name of the operation (e.g. "deposit", "withdraw"), when known. */ operationName?: string; item: BatchItemDescription; error: Hex; decodedError: DecodedSmartContractError[]; }>; simulationError?: { error: unknown; decoded: DecodedSmartContractError[]; }; accountStatusErrors?: Array<{ account: Address; error: Hex; decoded: DecodedSmartContractError[]; }>; vaultStatusErrors?: Array<{ vault: Address; error: Hex; decoded: DecodedSmartContractError[]; }>; /** * Tokens the batch overdraws from the real wallet, accounting for intra-batch * funding. Computed from the per-layer wallet balances: tracking the running * real balance (real on-chain balance + each step's net inflow/outflow), the * shortfall is the worst dip below zero across all steps. This nets out * self-funding (e.g. withdraw-then-deposit the same asset) and still catches a * step that consumes more than is genuinely available at that point. */ insufficientWalletAssets?: SimulationInsufficientRequirement[]; insufficientPermit2Allowances?: SimulationInsufficientRequirement[]; insufficientDirectAllowances?: SimulationInsufficientRequirement[]; } export type SimulateBatchOptions = { /** When true, fetches state overrides internally from the transaction plan before simulation. */ stateOverrides?: boolean; /** Additional state overrides supplied by higher-level planners. */ extraStateOverrides?: StateOverride; stateOverrideOptions?: SimulationStateOverrideOptions; vaultFetchOptions?: VaultFetchOptions; accountFetchOptions?: AccountFetchOptions; }; export type EstimateGasForTransactionPlanOptions = { /** When true, fetches state overrides internally from the transaction plan before gas estimation. */ stateOverrides?: boolean; stateOverrideOptions?: SimulationStateOverrideOptions; }; export type SimulationStateOverrideOptions = { /** Override the native (ETH) balance. Defaults to 1000 ETH. Set to 0n to skip. */ nativeBalance?: bigint; /** * Skip ERC20 balance overrides entirely. Use when the caller has already * validated that the account holds sufficient funds (e.g. UI form * validation). Drops per-call `balanceOf` and balance-slot discovery RPCs. */ noBalanceOverride?: boolean; /** * Skip ERC20 allowance overrides. Permit2 storage-slot overrides are * always emitted (they cost no RPC). Use when the caller knows the * account has already approved the relevant spenders. */ noAllowanceOverride?: boolean; /** * Caller-supplied wallet snapshot. Lets the SDK skip per-call balance/ * allowance RPCs when the supplied values already cover the requirement. */ wallet?: { balances?: Record; allowances?: Record<`${Address}:${Address}`, bigint>; }; /** * Caller-supplied storage-slot hints, owner-/spender-agnostic. When * present, the SDK derives slots cryptographically and bypasses * `eth_createAccessList` discovery. Pre-fetch with `fetchErc20SlotHints` * once per token and pass it on every simulate/estimate call to amortise. */ slotHints?: SlotHints; }; export type ExecutionSimulationContext = { deploymentService: IDeploymentService; walletService?: IWalletService; providerService?: ProviderService; vaultMetaService?: IVaultMetaService; priceService?: IPriceService; rewardsService?: IRewardsService; intrinsicApyService?: IIntrinsicApyService; eulerLabelsService?: IEulerLabelsService; abiService?: IABIService; describeBatch: (batch: readonly EVCBatchItem[]) => BatchItemDescription[]; }; export declare function deriveStateOverrides(ctx: ExecutionSimulationContext, chainId: number, account: Address, transactionPlan: TransactionPlan, options?: SimulationStateOverrideOptions): Promise; export declare function simulateTransactionPlan(ctx: ExecutionSimulationContext, chainId: number, account: Address, transactionPlan: TransactionPlan, options?: SimulateBatchOptions): Promise>; export declare function estimateGasForTransactionPlan(ctx: ExecutionSimulationContext, chainId: number, account: Address, transactionPlan: TransactionPlan, options?: EstimateGasForTransactionPlanOptions): Promise; export declare function extractBalanceRequirements(transactionPlan: TransactionPlan, account: Address): [Address, bigint][]; export {}; //# sourceMappingURL=simulate.d.ts.map