Skip to content
8 changes: 8 additions & 0 deletions src/interfaces/funds/IFund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ import {Order, State} from "../../libs/funds/Order.sol";
/// - create() and commit() NEVER reach ENDED - they advance the order forward (or revert)
/// - Only unlock() and recover() finalize orders by reaching ENDED
/// - Callers can batch state transitions when possible (e.g., create + commit if ACCEPTED)
///
/// Supported assets (binding on every adapter, current and future): the asset and share
/// tokens must be exact-transfer, delivering exactly the requested amount on every
/// transfer, burn, wrap or unwrap. An adapter may move an order's input in one transfer
/// or in several legs that sum to it (for example a Midas redeem's separate bond and
/// redemption legs), but each leg relies on that exactness, so a shortfall breaks the
/// venue request or the per-order accounting. Fee-on-transfer, rebasing and callback
/// tokens are out of scope and must not be admitted.
interface IFund {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
Expand Down
96 changes: 74 additions & 22 deletions src/interfaces/manager/rebalancer/IRetargetter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ interface IRetargetter {
/// @param whitelisted Whether the module is now whitelisted
event FlashLoanModuleSet(address indexed module, bool whitelisted);

/// @notice Emitted when the owner binds the instance to a position manager.
/// @param positionManager The position manager the instance is now bound to
event PositionManagerBound(address indexed positionManager);

/// @notice Emitted when the yield estimates are updated.
/// @param estimates The new estimates
event EstimatesSet(YieldEstimates estimates);
Expand All @@ -163,16 +167,18 @@ interface IRetargetter {
/* ASYNC FLOW */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice Starts an asynchronous retargetting operation by deploying a fresh Request.
/// @param positionManager The position manager to retarget (must match the bound pair)
/// @notice Starts an asynchronous retargetting operation against the bound position manager
/// by deploying a fresh Request.
/// @dev The repayment terms (horizon, tick duration, tick threshold) and the effective
/// yield cap are snapshotted into the operation here; a later setConfig does not
/// reprice a started operation. Reverts if no position manager is bound.
/// @param principal The intended principal, fail-fast checked against the cap
/// @param maxYieldBps_ The caller's yield cap; the effective cap is min(config, caller)
/// @param fund The whitelisted fund to bind to the operation
/// @param requestName The base name for the Request's PT/YT tokens
/// @param requestSymbol The base symbol for the Request's PT/YT tokens
/// @return request The deployed Request
function startRetargetting(
address positionManager,
uint256 principal,
uint16 maxYieldBps_,
address fund,
Expand All @@ -189,7 +195,9 @@ interface IRetargetter {
/// including the owner:
/// repayment prices every yield token from the origin, so capital arriving later would
/// be overpaid for time it never covered and, on a default, would dilute the earlier
/// lenders' recovery.
/// lenders' recovery. The principal cap is checked after the Request call, once the
/// minted amount sits in the PT supply, so a maker callback moving the cap mid-call
/// cannot leave the operation committed above it.
/// @param offer The signed offer
/// @param signature The maker's EIP-712 signature
/// @param ptAmount The principal amount to consume
Expand All @@ -207,7 +215,11 @@ interface IRetargetter {
/// directly, so the operator revokes leftovers once the funding round is over. A mint
/// front-running that revocation or the settlement itself is bounded by the yield gate
/// and the prorated accrual, and requires the consumer to have authorized the account
/// in the first place.
/// in the first place. A stored authorization is a firm commitment: the Request honors
/// it against the amounts fixed here even if the principal cap has fallen since, and
/// the caller (a CONSUMER_ROLE holder) picks the recipient freely, itself included. A
/// revocation that leaves the operation with no PT, no YT and no pending authorization
/// rewinds the loan clock to unstarted, reopening the consumption window.
/// @param to The account allowed to call the Request's mint
/// @param ptAmount The principal tokens to authorize; the account transfers this much asset
/// when it mints
Expand All @@ -217,9 +229,13 @@ interface IRetargetter {
/// @notice Pulls consumed funds from the operation's Request to the Retargetter.
/// @dev Pulling ends the funding round: every pending mint authorization is revoked and
/// capital entry (consume or new authorizations) stays closed for the rest of the
/// operation, so funds being deployed can no longer be diluted by late lenders.
/// operation, so funds being deployed can no longer be diluted by late lenders. A
/// zero amount is the named close transition: it moves no capital, still ends the
/// funding round the same way, and leaves the Request's balance pullable by later
/// calls.
/// @param amount The amount to pull; the full-balance sentinel `type(uint256).max` resolves
/// to the Request's whole balance
/// to the Request's whole balance, and zero closes the funding round without moving
/// capital
function pullRequestFunds(uint256 amount) external;

/// @notice Creates a fund order owned by and payable to the Retargetter.
Expand All @@ -234,6 +250,9 @@ interface IRetargetter {
function unlock() external returns (uint256 amountOut);

/// @notice Cancels the stored fund order and clears it.
/// @dev The fund must report the canceled order EMPTY; any other returned state keeps the
/// local order stored and reverts, so the Retargetter never forgets an order the
/// fund still processes.
function cancelOrder() external;

/// @notice Recovers the stored fund order's input; clears the order once ENDED.
Expand All @@ -260,6 +279,9 @@ interface IRetargetter {
function rebalance(RebalancingData calldata data) external;

/// @notice Trustlessly repays the operation's Request with the formula-computed amount.
/// @dev Transfers only the shortfall between the owed amount and the Request's balance, and
/// the owed yield floors at one full tick, so an operation whose consumed principal
/// was never pulled still tops up that tick from position-derived funds.
/// @return owedAmount The amount owed and settled
function repay() external returns (uint256 owedAmount);

Expand All @@ -278,30 +300,32 @@ interface IRetargetter {
/* SYNC FLOW */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice Runs a whole retargetting operation atomically inside a flash loan.
/// @notice Runs a whole retargetting operation against the bound position manager atomically
/// inside a flash loan.
/// @dev The steps are supplied as a multicall payload executed inside the flash-loan
/// callback. The callback is bound to this exact payload (digest match) and requires
/// the loan delivered in full before any step runs. At window close no order may be
/// stored and the Retargetter's balances of both bound assets must be within the
/// configured residual tolerance.
/// @param positionManager The position manager to retarget (must match the bound pair)
/// configured residual tolerance. Reverts if no position manager is bound. The position
/// manager's rebalance cooldown is enforced per rebalance call, so with a nonzero
/// cooldown compatible legs must be packed into a single rebalance step, and a route
/// needing an intervening fund action between two rebalances runs as separate
/// transactions.
/// @param flashLoanModule The whitelisted flash-loan module to borrow through
/// @param flashLoanAmount The flash-loan size, checked against the principal cap
/// @param fund The whitelisted fund the payload may create orders against
/// @param data The step calls executed inside the flash-loan window
function startSyncRetargetting(
address positionManager,
address flashLoanModule,
uint256 flashLoanAmount,
address fund,
bytes[] calldata data
) external;
function startSyncRetargetting(address flashLoanModule, uint256 flashLoanAmount, address fund, bytes[] calldata data)
external;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONFIGURATION */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice Sets the full configuration (scalars and estimates).
/// @dev Changes take effect immediately, except for an in-flight operation's repayment
/// terms: the horizon, tick duration and tick threshold, like the yield cap, are
/// snapshotted at operation start and a running operation settles on those.
/// @param config_ The new configuration, validated against the documented bounds
function setConfig(RetargetterConfig calldata config_) external;

Expand All @@ -324,11 +348,23 @@ interface IRetargetter {
/// @param whitelisted Whether the module should be whitelisted
function setFlashLoanModule(address module, bool whitelisted) external;

/// @notice Rotates (or clears) the single position manager every operation runs against.
/// @dev The instance's asset pair is derived from the first position manager ever bound (at
/// initialize or here) and is immutable after; this rotates the binding to another
/// position manager on that pair, or unbinds it with the zero address to abandon the
/// instance (operations then revert until a same-pair manager is bound again).
/// Operations never take a position manager as an argument, so every start-gate and
/// principal-cap read trusts an owner-curated address rather than a caller-supplied
/// one. A nonzero manager is checked to be a contract on the bound pair; both paths
/// revert while an operation is active (the binding can only change while idle).
/// @param positionManager The position manager to bind, or the zero address to unbind
function setPositionManager(address positionManager) external;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* VIEWS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

/// @notice Returns the bound asset pair, set once at initialization.
/// @notice Returns the bound asset pair, derived from the position manager at initialization.
/// @return collateralAsset The collateral asset of the pair
/// @return debtAsset The debt asset of the pair
function assets() external view returns (address collateralAsset, address debtAsset);
Expand All @@ -345,6 +381,10 @@ interface IRetargetter {
/// (zero inside a SYNC window); the loan clock can only start while at least the
/// minimum deadline buffer remains before it
/// @return operationMaxYieldBps The effective per-operation yield cap
/// @return horizon The operation's snapshotted yield annualization basis
/// @return tickDuration The operation's snapshotted repayment granularity
/// @return tickThreshold The operation's snapshotted tick grace and consumption window
/// length
/// @return order The stored fund order rebuilt in memory
/// @return orderLive Whether a fund order is stored
function operation()
Expand All @@ -357,6 +397,9 @@ interface IRetargetter {
uint40 startedAt,
uint40 repaymentDeadline,
uint16 operationMaxYieldBps,
uint32 horizon,
uint24 tickDuration,
uint24 tickThreshold,
Order memory order,
bool orderLive
);
Expand All @@ -365,12 +408,18 @@ interface IRetargetter {
/// flash-loan window for the duration of its transaction).
function isActive() external view returns (bool);

/// @notice Computes the current principal cap for a position manager.
/// @notice Computes the current principal cap for the bound position manager.
/// @dev Quoter formula on live state with the owner estimates, auto-detected direction,
/// times one plus the principal buffer.
/// @param positionManager The position manager to size against
/// times one plus the principal buffer. Below target the cap is further bounded by
/// the one-trip repayment bound, so a cap-sized, fully deployed operation can always
/// borrow its worst permitted repayment back at target in a single settlement trip.
/// This view sizes that bound on the config yield-cap ceiling, a safe floor for
/// every permissible operation; the operation gates size it on the effective
/// per-operation cap (zero for a SYNC window, whose flash repayment carries no
/// yield) and can admit up to that looser bound. Reverts if no position manager is
/// bound.
/// @return The principal cap in debt-asset units
function maxPrincipal(address positionManager) external view returns (uint256);
function maxPrincipal() external view returns (uint256);

/// @notice Returns the current amount owed on the active operation's Request.
function owed() external view returns (uint256);
Expand All @@ -394,6 +443,9 @@ interface IRetargetter {
/// @param module The module to check
function isFlashLoanModule(address module) external view returns (bool);

/// @notice Returns the bound position manager (zero when none is bound).
function boundPositionManager() external view returns (address);

/// @notice Returns the quoter holding the sizing math.
function quoter() external view returns (address);

Expand Down
27 changes: 27 additions & 0 deletions src/interfaces/manager/rebalancer/IRetargetterQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,33 @@ interface IRetargetterQuoter {
uint256 duration
) external pure returns (uint256 principal, uint256 collateralToFreeQuoted);

/// @notice Computes the one-trip repayment bound of an LTV-up operation.
/// @dev The largest principal whose worst permitted repayment (the principal plus the full
/// flat yield cap) can still be borrowed at target once the subscription has landed as
/// collateral: `P * (1 + yieldCap) <= targetLtv * (K * (1 + Yc) + P) - D * (1 + Rb)`.
/// Same accrual conventions as `ltvUpPrincipal`; the bridge yield estimate is replaced
/// by the flat cap because repayment prices actual YT supply, not the estimate.
/// Returns 0 when the position is at or above target once the accruals are applied.
/// Reverts with `InvalidParameters` only at a full target LTV combined with a zero
/// yield cap.
/// @param collateralQuoted The aggregate collateral value in debt-asset units
/// @param debt The aggregate debt in debt-asset units
/// @param targetLtv The target loan-to-value ratio (WAD)
/// @param maxYieldBps The flat yield cap bounding the operation's YT supply (basis points)
/// @param borrowRate The venue borrow rate on existing debt (WAD per 365 days)
/// @param collateralYieldRate The collateral yield rate (WAD per 365 days)
/// @param duration The expected subscription settlement duration in seconds
/// @return principal The one-trip repayment bound in debt-asset units (rounded down)
function ltvUpOneTripPrincipal(
uint256 collateralQuoted,
uint256 debt,
uint256 targetLtv,
uint256 maxYieldBps,
uint256 borrowRate,
uint256 collateralYieldRate,
uint256 duration
) external pure returns (uint256 principal);

/// @notice Quantizes an elapsed loan duration to whole repayment ticks.
/// @dev From the first second the borrower owes one full tick; the count promotes to the
/// next tick each time the elapsed duration passes a whole number of ticks plus the
Expand Down
4 changes: 4 additions & 0 deletions src/libs/manager/rebalancer/LibRetargetterErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ library LibRetargetterErrors {
/// @notice Thrown when binding a fund that is not owner-whitelisted.
error FundNotWhitelisted();

/// @notice Thrown when starting an operation while the position manager is unbound (the
/// instance was abandoned by binding the zero address).
error PositionManagerNotBound();

/// @notice Thrown when using a flash-loan module that is not owner-whitelisted.
error ModuleNotWhitelisted();

Expand Down
Loading