Summary
MultiAccount manages legacy PartyA sub-accounts and routes calls for their owners.
SymmioPartyB routes PartyB and solver calls. Both helper contracts accepted an ambient signal in place of a real
authorization check: a global flag on the core diamond saying the protocol was currently inside an InstantLayer batch. v0.8.6
deletes that alternative from MultiAccount and replaces it with a direct-caller role check in SymmioPartyB.
What changed
MultiAccount._call gates non-owner callers on a per-account, per-selector delegation map, and it accepted an
alternative:
require(
delegatedAccesses[account][msg.sender][functionSelector] || ISymmio(symmioAddress).isCallFromInstantLayer(),
"MultiAccount: Unauthorized access"
);
isCallFromInstantLayer() reads a single global boolean on the core diamond,
GlobalAppStorage.callFromInstantLayer. The InstantLayer sets it true before executing a batch and false
afterwards.
That flag answers "is the protocol currently inside an InstantLayer batch?" It says nothing about who is
calling MultiAccount. While it is true, _call accepts any msg.sender, for any account,
for any selector, and the delegation map is skipped entirely. InstantLayer's whitelistedTargets map identifies
the external contracts a batch is allowed to call. A v0.8.5 batch could call one of those targets; any code reached while that
call was executing observed the same core-wide flag. During that window the delegation check was therefore off for every
MultiAccount sub-account at once. The v0.8.5 flag was persistent state, not automatically reset, and its lifecycle was the
caller's responsibility.
| Contract | Before | After |
|---|---|---|
MultiAccount._call |
delegation entry or isCallFromInstantLayer() |
delegation entry only |
SymmioPartyB._call, _multicastCall, adlClose, and
setFundingFee
|
isCallFromInstantLayer() |
adlClose and setFundingFee accept MANAGER_ROLE,
TRUSTED_ROLE, or core INSTANT_LAYER_ROLE. Routed calls apply per-leg gates:
restricted core selectors require MANAGER_ROLE; other core selectors accept manager, trusted, or
core InstantLayer role; non-core destinations must be whitelisted and require TRUSTED_ROLE.
|
Where core INSTANT_LAYER_ROLE is accepted, SymmioPartyB checks that role on the
direct caller. This binds authorization to msg.sender instead of a core-wide execution flag. The role
does not bypass the stricter manager-only selector gate or the trusted-plus-whitelist gate for non-core destinations.
Effect on callers
Anything that reached a MultiAccount sub-account during an InstantLayer batch without holding a delegation entry now reverts
with MultiAccount: Unauthorized access. The resolution is an explicit delegateAccess grant for the
account and selector. The account owner creates that entry by calling delegateAccess(account, target, selector),
or delegateAccesses for several selectors.
Here, target is the contract that will call MultiAccount directly, normally the deployed InstantLayer, not the
inner Symmio or AccountLayer destination. For SymmioPartyB, granting that deployed InstantLayer core
INSTANT_LAYER_ROLE authorizes only the direct entrypoints and non-restricted core calls that accept the role. It
does not authorize restricted core selectors or non-core multicast destinations.
MultiAccount is a legacy contract. AccountLayer registers deployed instances under legacyMultiAccounts, resolves
ownership through them, and exposes legacy-account discovery views. Existing MultiAccount integrations must therefore use
explicit delegateAccess entries rather than relying on an InstantLayer batch window.