pico-quorum

Safe{Wallet}: GS021 when a confirmation is a contract signature

The Safe web app can’t execute a transaction that a PicoQuorum key approved once there are two or more signatures. It fails with GS021 because the chip’s approval is an EIP-1271 contract signature, and the web app rebuilds every stored confirmation as a plain signature. Until this is fixed upstream, tools/quorum live exec executes instead.

Jason submits everything to Safe by hand. This folder holds the drafts and the evidence:

File What
ISSUE.md The issue, ready to paste: title plus body in their bug-report template. The reproduction script is inlined in a <details> block
PR.md The pull request, ready to paste: their PR template, with the Mermaid summary they require for AI-authored PRs and the CLA line. Replace #ISSUE with the issue number
repro/ repro.mjs, the self-contained reproduction (how to run it): Anvil fork of Base Sepolia, throwaway keys, protocol-kit’s own buildSignatureBytes for both layouts. It also prints the test fixtures (--fixture)
0001-fix-keep-EIP-1271-contract-signatures-when-rebuildin.patch The fix and its test, one commit made with git format-patch against their dev at 5717e3a. This is the pre-review version; see Status for what was submitted

Before submitting: privacy

ISSUE.md, PR.md and the patch name no personal address, Safe or transaction. The patch’s test fixture comes from throwaway keys on a local fork. Keep it that way:

Status

What was submitted differs from these drafts. The versions on GitHub are the reference:

Waiting on:

Working on the fork

# after forking on GitHub
git clone https://github.com/jmcpheron/safe-wallet-monorepo.git && cd safe-wallet-monorepo
git remote add upstream https://github.com/safe-global/safe-wallet-monorepo.git
git fetch upstream
git checkout -b fix/contract-signatures-existing-tx upstream/dev

# the fix, as a commit with its original message and authorship
git am ../pico-quorum/docs/upstream/safe-wallet-gs021/0001-fix-keep-EIP-1271-contract-signatures-when-rebuildin.patch
# once the issue exists, add its number:
git commit --amend      # add a line "Fixes #NNNN" above "Refs #3673"

corepack enable && yarn install
yarn workspace @safe-global/utils test src/utils/__tests__/safeTransaction.test.ts
yarn workspace @safe-global/web test src/services/tx/tx-sender
yarn workspace @safe-global/utils type-check && yarn workspace @safe-global/web type-check && yarn workspace @safe-global/mobile type-check
yarn workspace @safe-global/utils lint && yarn workspace @safe-global/web lint
yarn prettier

git push -u origin fix/contract-signatures-existing-tx
# open the PR from the fork's branch into safe-global:dev, with PR.md as the description

If git am doesn’t apply cleanly because dev has moved on, git am --3way usually does. The change is small, so it can also be redone by hand: the helper and its test, plus two call sites.

Review, 2026-09-25. A review of the drafts led to:

All adopted. The Safe.sol link now points at the GS021 check itself (L306-L316); the suggested range stopped short of it.

What’s been verified so far:

How we know

Evidence log (private: names personal addresses, don’t paste upstream)

Add to this as it comes in: date, chain, what was seen, links.

2026-09-25, Base Sepolia

2026-09-25, Base mainnet

2026-09-25, a private fork of Base Sepolia: the public fixture