Summary
Three AccountLayer behaviors now produce different caller-visible results. Existing entry points remain, but integrations may see a new aggregate read API, a different deletion error when a sub-account has multiple blockers, and partial-fill child handling in the open-position hook.
Aggregate affiliate views
Reading an affiliate's configuration previously meant stitching together getAffiliateState,
getAffiliateAdmin, getAffiliateFeeDistributor, getAffiliateSymmioCores,
getAffiliateStakeholders, and getAffiliateSymmioShare. Those six calls could observe different
states if anything changed between them. v0.8.6 adds two aggregate reads.
function getAffiliate(address affiliate) external view returns (AffiliateDetail memory);
function getAffiliateSelectorConfigs(address affiliate, bytes4[] calldata selectors)
external view returns (AffiliateSelectorConfig[] memory);
AffiliateDetail mirrors the stored AffiliateData with the fee details flattened, and adds the
affiliate's own address and its pendingFeeUpdate. That field reports a requested fee split waiting for approval,
including whether it exists, its timestamp, the proposed Symmio share, and proposed stakeholders. It previously had no
aggregate reader. An unregistered affiliate reads back as an empty struct with state NONE rather than reverting.
Per-selector data is not included, because hooks and both allow-lists live in mappings and cannot be enumerated from on-chain
state. getAffiliateSelectorConfigs takes the selectors you care about and returns the hook address,
hookAllowed, and callAllowed for each, in the order requested. hookAllowed says whether
the selector may trigger the configured affiliate hook. callAllowed says whether the affiliate may submit that
selector through callAsAffiliate.
Sub-account deletion reports the real blocker
deleteSubAccount checks that an account is empty before removing it. The order of those checks changed, and with
it the error a blocked caller receives.
| Order | Before v0.8.6 | After |
|---|---|---|
| 1 | balance / allocated balance | open positions |
| 2 | open positions | pending quotes |
| 3 | pending quotes | balance / allocated balance |
With an open position or pending quote, the old order returned SubAccountNotEmpty before reaching the specific
trading-state check. Each position or quote keeps its credit valuation adjustment (CVA) and liquidation fee (LF) locked as
liquidation collateral in allocated balance, and core forbids deallocating below that floor, so such an account can never be
emptied first. The error pointed the caller at a withdrawal that could not succeed instead of naming the position or quote
that had to be closed.
Callers now receive OpenPositionsExist or PendingQuotesExist in those cases.
SubAccountNotEmpty is still raised, but only when the account genuinely has nothing but a balance left. Error
handling that branches on SubAccountNotEmpty to prompt a withdrawal needs to handle the other two.
The open-position hook now handles partial-fill children
SymmioHookFacet.onOpenPosition was a deliberate no-op. It existed so core would not revert when calling the hook.
It now updates virtual accounts.
When a quote is partially filled, core opens the filled portion and creates a child quote carrying the remainder.
That child was never registered against the virtual account, so a virtual account's quoteIds set silently lost
track of the leftover inventory. MARKET and direction-isolated virtual accounts may track multiple compatible quotes, while a
POSITION-isolated virtual account permits only one position. The hook now reads the newest quote and checks that it is the
pending child of the quote just opened and belongs to the same PartyA. MARKET and direction-isolated accounts add the child to
the set. POSITION-isolated accounts cancel it immediately so the same account cannot later open a second position.
Supporting the hook required three core interface changes. getNextQuoteId became a view function so
it can be called from a hook, getQuote was added so the candidate child can be inspected before it is trusted,
and requestToCancelQuote lets the POSITION path cancel the verified child as the virtual account.
See Position Isolation Partial Fill Remainder for the POSITION path, including its pause and instant-actions-mode behavior.
The hook returns early for anything that is not an existing virtual account, so sub-accounts and legacy accounts are unaffected.