Problem
fromSafeWebauthn takes a single `isInit: boolean` at construction and returns a `Signer` with a fixed `address` resolved against that `isInit` (shared signer for `true`, deterministic verifier-proxy for `false`).
This works for the homogeneous-init case — all UserOps in a multichain bundle share the same `isInit` value (e.g., a brand-new Safe being deployed on N chains in one bundle, or an already-deployed Safe sending follow-up ops on N chains).
It does not work for the mixed-init case — chain A is already deployed (`isInit=false`, owner = verifier proxy at one address) while chain B is fresh (`isInit=true`, owner = shared signer at a different address). The two ops need different per-op signer addresses, but `SafeMultiChainSigAccountV1.signUserOperationsWithSigners` only accepts a flat `signers` array — one signer applied uniformly to every op.
This is a realistic scenario: any time a passkey-owned multichain Safe expands to a new chain, the first cross-chain bundle on that new chain has the deployment op (isInit=true) alongside follow-up ops on the older chains (isInit=false).
Affected code
- `src/account/Safe/adapters.ts` — `fromSafeWebauthn()`
- `src/account/Safe/SafeMultiChainSigAccount.ts` — `signUserOperationsWithSigners()`
Possible API shapes
Option A: signer factory
`fromSafeWebauthn` returns a function that the plural method calls per-op:
```ts
const signerFactory = fromSafeWebauthn({ publicKey, accountClass, getAssertion })
// SDK invokes per UserOperationToSign:
// const signer = signerFactory({ isInit: uop.userOperation.nonce === 0n })
```
Option B: union type for the signers param
`signUserOperationsWithSigners` accepts either a fixed signer (homogeneous) or a per-op resolver (mixed):
```ts
signUserOperationsWithSigners(
opsToSign: UserOperationToSign[],
signers: ReadonlyArray<Signer | ((op: UserOperationToSign) => Signer)>,
)
```
Option A is cleaner; Option B is less invasive. Both compose with the existing #155 fix.
Workaround today
Bypass `signUserOperationsWithSigners` entirely: compute `getMultiChainSingleSignatureUserOperationsEip712Hash` yourself, call the adapter's `signHash` directly, then use `formatSignaturesToUseroperationsSignatures` with per-op `webAuthnSignatureOverrides.isInit`. This is what `abstractionkit-examples/chain-abstraction/add-owner-passkey.ts` did before #155 landed; an earlier comment on #153 (#153 (comment)) already flagged this scope gap.
Out of scope for this issue
Problem
fromSafeWebauthntakes a single `isInit: boolean` at construction and returns a `Signer` with a fixed `address` resolved against that `isInit` (shared signer for `true`, deterministic verifier-proxy for `false`).This works for the homogeneous-init case — all UserOps in a multichain bundle share the same `isInit` value (e.g., a brand-new Safe being deployed on N chains in one bundle, or an already-deployed Safe sending follow-up ops on N chains).
It does not work for the mixed-init case — chain A is already deployed (`isInit=false`, owner = verifier proxy at one address) while chain B is fresh (`isInit=true`, owner = shared signer at a different address). The two ops need different per-op signer addresses, but `SafeMultiChainSigAccountV1.signUserOperationsWithSigners` only accepts a flat `signers` array — one signer applied uniformly to every op.
This is a realistic scenario: any time a passkey-owned multichain Safe expands to a new chain, the first cross-chain bundle on that new chain has the deployment op (isInit=true) alongside follow-up ops on the older chains (isInit=false).
Affected code
Possible API shapes
Option A: signer factory
`fromSafeWebauthn` returns a function that the plural method calls per-op:
```ts
const signerFactory = fromSafeWebauthn({ publicKey, accountClass, getAssertion })
// SDK invokes per UserOperationToSign:
// const signer = signerFactory({ isInit: uop.userOperation.nonce === 0n })
```
Option B: union type for the signers param
`signUserOperationsWithSigners` accepts either a fixed signer (homogeneous) or a per-op resolver (mixed):
```ts
signUserOperationsWithSigners(
opsToSign: UserOperationToSign[],
signers: ReadonlyArray<Signer | ((op: UserOperationToSign) => Signer)>,
)
```
Option A is cleaner; Option B is less invasive. Both compose with the existing #155 fix.
Workaround today
Bypass `signUserOperationsWithSigners` entirely: compute `getMultiChainSingleSignatureUserOperationsEip712Hash` yourself, call the adapter's `signHash` directly, then use `formatSignaturesToUseroperationsSignatures` with per-op `webAuthnSignatureOverrides.isInit`. This is what `abstractionkit-examples/chain-abstraction/add-owner-passkey.ts` did before #155 landed; an earlier comment on #153 (#153 (comment)) already flagged this scope gap.
Out of scope for this issue