pico-quorum

Title: fix: keep EIP-1271 contract signatures when rebuilding a queued tx (GS021)


What it solves

Resolves: #ISSUE. #3673 appears to have the same underlying cause.

In a Safe with a threshold of 2 or more, Safe{Wallet} can’t simulate or execute a queued transaction when a confirmation the Safe has to check is an EIP-1271 contract signature. The failure is GS021 and “Cannot estimate”. I reproduced it with a passkey signer (SafeWebAuthnSignerProxy). #3673 reports it for a nested Safe owner.

How this PR fixes it

How to test it

Unit tests (yarn workspace @safe-global/utils test src/utils/__tests__/safeTransaction.test.ts, 11 tests):

The unit tests check encoding only. The fixtures come from a separate fork experiment:

Manual:

  1. Use a Safe with a threshold of 2 whose owners include an EOA and a contract owner.
  2. Propose in Safe{Wallet} and sign with the EOA.
  3. Confirm with the contract owner through the Transaction Service.
  4. Execute in Safe{Wallet}. Simulation should pass and the transaction should execute. Before this change it fails with GS021.

Affected flows

Blast radius

Risks / not checked

Visual summary

flowchart LR
  S[Confirmation from the Transaction Service] -->|EOA, eth_sign, v = 1| P[EthSafeSignature, plain]
  S -->|v = 0: owner, offset, 00, length, data| C[EthSafeSignature, contract, data only]
  P --> B[buildSignatureBytes]
  C --> B
  B -->|static parts, then dynamic parts at offsets past them| E[correctly encoded signatures]
  S -. before: every confirmation plain .-> X[offset stays 65, inside the static region when threshold ≥ 2: GS021]

Drafted with AI assistance; I reviewed the changes. Testing and limitations are documented above.

Checklist


CLA signature

With the submission of this Pull Request, I confirm that I have read and agree to the terms of the Contributor License Agreement.