Skip to main content

SwappingYieldForwarder

Git Source

Inherits: YieldForwarder

Title: SwappingYieldForwarder

Author: Golem Foundation

Extends YieldForwarder with an additional swap step before forwarding to the receiver.

Inherits reportAndForward() from YieldForwarder (no-swap fallback) and adds reportSwapAndForward() which swaps via a pluggable ISwapper before forwarding. This dual-mode design acts as a built-in circuit breaker: if the swap protocol is unavailable, the keeper simply calls the inherited non-swapping path instead. CALL CHAIN (with swap): Keeper EOA -> reportSwapAndForward() -> strategy.report() -> profit shares minted to this contract -> redeem shares to self -> swap via ISwapper -> target asset forwarded to receiver CALL CHAIN (without swap / fallback): Keeper EOA -> reportAndForward() [inherited] -> strategy.report() -> profit shares minted to this contract -> redeem shares directly to receiver DESIGN:

  • Keeper-gated: only the designated keeper can trigger yield forwarding
  • Single-purpose: assets can only flow to the hardcoded receiver
  • Pluggable swap: ISwapper adapter handles DEX-specific logic; the adapter itself is settable by the vault's management() role so deeper pools or newer adapter versions can be adopted without redeploying the forwarder
  • Dual-mode: keeper picks swap vs no-swap path at call-time
  • Strategy is passed as a call-time parameter to avoid circular dependencies

Note: security-contact: [email protected]

Quick Start - What Matters Most

Use this when donation shares should be redeemed from the strategy, swapped into a target asset, and forwarded to a receiver.

Key functions to understand:

  • reportSwapAndForward(strategy, maxLoss, minAmountOut, deadline) - Reports, redeems, swaps, and forwards
  • setSwapper - Vault management changes the adapter
  • setMinSlippageBps - Vault management sets the global slippage floor
  • forwardToken - Keeper or vault management can move residual tokens onward

State Variables

targetAsset

The desired output token after swapping

Set once at construction, cannot be changed

address public immutable targetAsset

vault

The governance source for this forwarder

Calls to vault.management() at swapper-management time authorize the caller. In a 1:1 forwarder-to-vault deployment this is the tokenized strategy whose profit shares flow through this forwarder; vault management is the same multisig that already controls keeper/dragon-router rotation.

address public immutable vault

swapper

The swap adapter used to convert underlying -> target asset

Settable by vault.management() to pivot to a different adapter (e.g. a new pool tier, a new DEX version) without redeploying.

ISwapper public swapper

MAX_BPS

Denominator for basis-point calculations (10_000 = 100%)

uint16 public constant MAX_BPS = 10_000

minSlippageBps

Floor on minAmountOut, denominated as basis points of assetsIn.

Set at construction so the deployment config explicitly chooses whether the floor is active from the first report. A value of 0 is an intentional opt-out for routes where management relies on the keeper's oracle-informed minAmountOut instead of a near-parity floor.

uint16 public minSlippageBps

Functions

onlyVaultManagement

Restricts a call to the vault's management() address. Reading this at call time (instead of caching a local admin) means rotation via the vault's own two-step transfer (setPendingManagement / acceptManagement) is picked up automatically.

modifier onlyVaultManagement() ;

constructor

Creates a new SwappingYieldForwarder

constructor(
address _receiver,
address _keeper,
address _targetAsset,
address _swapper,
address _vault,
uint16 _minSlippageBps
) YieldForwarder(_receiver, _keeper);

Parameters

NameTypeDescription
_receiveraddressAddress that will receive all forwarded assets
_keeperaddressAddress authorized to call reportAndForward / reportSwapAndForward
_targetAssetaddressAddress of the desired output token after swapping
_swapperaddressInitial ISwapper implementation for DEX routing
_vaultaddressVault contract whose management() controls swapper rotation
_minSlippageBpsuint16Initial floor in basis points of assetsIn (0 disables)

setSwapper

Replace the active swap adapter

Authorized by vault.management(). The new adapter must expose the ISwapper pull-pattern semantic; the forwarder enforces its own minAmountOut re-check inside reportSwapAndForward as a defence-in-depth guard against a buggy adapter.

function setSwapper(address _newSwapper) external onlyVaultManagement;

Parameters

NameTypeDescription
_newSwapperaddressNew ISwapper implementation

setMinSlippageBps

Update the slippage floor applied to the keeper's minAmountOut

Authorized by vault.management(). Passing 0 disables the floor.

function setMinSlippageBps(uint16 _bps) external onlyVaultManagement;

Parameters

NameTypeDescription
_bpsuint16New floor in basis points of assetsIn (max 10_000)

reportSwapAndForward

Calls report() on the strategy, redeems profit shares, swaps the underlying asset to the target asset via the configured ISwapper, and forwards to the receiver

The swap path: redeem to self -> approve swapper -> swapper.swap() -> receiver. The swapper enforces minAmountOut internally; the forwarder additionally re-checks the reported output against minAmountOut as defence-in-depth in case a future/buggy adapter forgets its own check. If report() produces no profit shares, the function returns 0 without reverting. If redemption returns 0 assets, the function returns 0 without attempting a swap. The caller MUST supply a fresh deadline (seconds since epoch). The underlying Uniswap V3 adapter uses block.timestamp as the router deadline, which always passes at inclusion and therefore adds no real time bound between the keeper's quote and settlement. Enforcing an explicit expiry at the forwarder prevents stale-slippage execution when a queued transaction lands in a later block against a moved market. There is no 0 = disabled sentinel: keepers always pass block.timestamp + buffer at transaction assembly time.

function reportSwapAndForward(address strategy, uint256 maxLoss, uint256 minAmountOut, uint256 deadline)
external
nonReentrant
returns (uint256 assetsOut);

Parameters

NameTypeDescription
strategyaddressAddress of the strategy contract (must implement IReportable, IRedeemable, IERC20, IERC4626Asset)
maxLossuint256Maximum acceptable loss in basis points for the redemption
minAmountOutuint256Minimum acceptable amount of target asset after swap (slippage protection)
deadlineuint256Unix timestamp (seconds) past which the call reverts with ExpiredDeadline

Returns

NameTypeDescription
assetsOutuint256Amount of target asset forwarded to receiver (0 if no profit shares)

_authorizeForwardToken

Authorizes forwardToken callers. Derived forwarders can extend this when they have an additional governance source.

function _authorizeForwardToken() internal view override;

Events

YieldSwappedAndForwarded

Emitted when shares are redeemed, swapped to target asset, and forwarded

event YieldSwappedAndForwarded(
address indexed strategy, address indexed receiver, uint256 shares, uint256 assetsIn, uint256 assetsOut
);

Parameters

NameTypeDescription
strategyaddressAddress of the strategy whose shares were redeemed
receiveraddressAddress that received the target assets
sharesuint256Amount of shares redeemed
assetsInuint256Amount of underlying assets redeemed (swap input)
assetsOutuint256Amount of target assets forwarded (swap output)

SwapperUpdated

Emitted when the swap adapter is replaced by vault management

event SwapperUpdated(address indexed oldSwapper, address indexed newSwapper);

Parameters

NameTypeDescription
oldSwapperaddressThe previous ISwapper implementation
newSwapperaddressThe newly installed ISwapper implementation

MinSlippageBpsUpdated

Emitted when the admin-set slippage floor is updated

event MinSlippageBpsUpdated(uint16 oldBps, uint16 newBps);

Parameters

NameTypeDescription
oldBpsuint16Previous floor (basis points of assetsIn)
newBpsuint16New floor

Errors

InvalidSwapper

Thrown when the swapper address is zero

error InvalidSwapper();

InvalidTargetAsset

Thrown when the target asset address is zero

error InvalidTargetAsset();

InvalidVault

Thrown when the vault address is zero

error InvalidVault();

OnlyVaultManagement

Thrown when a swapper-management function is called by a non-management address

error OnlyVaultManagement();

InsufficientSwapOutput

Thrown when a swapper reports an output below the enforced minimum

error InsufficientSwapOutput(uint256 expected, uint256 actual);

Parameters

NameTypeDescription
expecteduint256Minimum amount expected
actualuint256Amount actually reported

InvalidSlippageBps

Thrown when setMinSlippageBps is called with a value above MAX_BPS

error InvalidSlippageBps();

SlippageFloorTooLoose

Thrown when the caller's minAmountOut falls below the admin-set floor

error SlippageFloorTooLoose(uint256 floor, uint256 supplied);

Parameters

NameTypeDescription
flooruint256Required minimum (assetsIn * minSlippageBps / MAX_BPS)
supplieduint256The minAmountOut the keeper passed in

ExpiredDeadline

Thrown when reportSwapAndForward is called past the caller-supplied deadline

error ExpiredDeadline(uint256 deadline, uint256 blockTimestamp);

Parameters

NameTypeDescription
deadlineuint256The keeper-chosen expiry timestamp (seconds since epoch)
blockTimestampuint256The block time at which the call landed