Skip to content

fix: harden delegated signatures and add Foundry tooling#12

Merged
boyuanx merged 18 commits into
mainfrom
docs/security-hardening-plan
Jul 2, 2026
Merged

fix: harden delegated signatures and add Foundry tooling#12
boyuanx merged 18 commits into
mainfrom
docs/security-hardening-plan

Conversation

@boyuanx

@boyuanx boyuanx commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds B1 delegated-signature hardening for SP v1.1.4: all delegated flows now use an EIP-712 authorization envelope while preserving the existing state-changing function ABIs.
  • Adds per-attester delegated nonces, uint64 signature deadlines, and domain binding to chainId + verifyingContract; old raw delegated signatures are intentionally rejected.
  • Adds Foundry deployment scripts for deterministic SP implementation/proxy deployment through CreateX CREATE3 using the Built-by-Sign foundry-deployer helper.
  • Adds an existing-proxy UUPS upgrade script backed by the official Sign address book proxy list.
  • Adds bun run patch:sp as a chain-aware wrapper for patching existing deployments without manually setting Alchemy RPC URLs or proxy addresses per chain.
  • Hardens deployment/upgrade scripts after review: proxy ownership handoff is fail-closed for configured mainnets and PROD_OWNER deployments, upgrades assert the ERC-1967 implementation slot and resolved version(), no-op upgrades without calldata are rejected, and unreadable/empty version() checks are hard failures.
  • Removes legacy Hardhat, ZKSync Hardhat, TypeScript deploy, Prettier, Solhint, and npm-specific package tooling now that deployment/build/test flows are Foundry + Bun.

Delegated Signature Migration

  • Existing state-changing function selectors are unchanged.
  • Integrations that use delegated flows must stop passing a raw 65-byte signature as delegateSignature.
  • New delegated flow:
    • read nonce = delegationNonces(delegateAttester);
    • compute the action hash using the existing getDelegated*Hash(...) helper;
    • sign getDelegatedAuthorizationDigest(delegateAttester, actionHash, nonce, deadline);
    • pass delegateSignature = abi.encode(nonce, deadline, signature).
  • Non-delegated flows are unchanged.
  • This PR intentionally fixes replay/domain safety without changing the existing action-hash payload shape. A stricter follow-up can bind delegated signatures to the selected overload, fee params, indexingKey, and extraData, but that is a delegated signing semantics change for SDKs and relayers.

Deployment / Upgrade Plan

  • Recommended wrapper flow: run bun run patch:sp -- <chain> to simulate, then bun run patch:sp -- <chain> --broadcast to patch.
  • The wrapper resolves Alchemy RPC URLs from ALCHEMY_API_KEY, uses the official address-book proxy for the selected chain, verifies RPC chain ID and proxy code, checks owner/version, and invokes the existing Foundry scripts.
  • Chains not available through Alchemy use chain-specific RPC env vars such as PLUME_TESTNET_RPC_URL, or --rpc-url.
  • If the proxy owner is not the deployment signer, the wrapper does not attempt a direct upgrade. With --broadcast, it deploys/resolves the implementation and prints the upgradeToAndCall(implementation, 0x) calldata for the owner contract or Safe.
  • Deploy the new SP implementation with script/DeploySPImplementation.s.sol using the same DEPLOYER and SP_IMPL_SALT_ID across chains so the implementation address is consistent when bytecode is identical.
  • For new networks, deploy the proxy with script/DeploySPProxy.s.sol; it initializes SP.initialize(INITIAL_SCHEMA_COUNTER, INITIAL_ATTESTATION_COUNTER).
  • If PROD_OWNER is set, fresh proxy deployment transfers ownership to PROD_OWNER even when the chain is not listed in MAINNET_CHAIN_IDS. If the chain is listed in MAINNET_CHAIN_IDS, PROD_OWNER is required.
  • Existing proxy addresses are sourced from https://docs.sign.global/for-builders/address-book and encoded in script/SPProxyRegistry.sol. Deprecated, struck-through address-book entries are excluded from batch upgrades; use SP_PROXY for any non-address-book or deprecated proxy.
  • Upgrade script postconditions verify the proxy's ERC-1967 implementation slot and version(). By default, the expected version is read from the resolved implementation; set SP_EXPECTED_VERSION only when you want an explicit override. Missing or empty implementation/proxy version() now reverts.

Review Follow-Up

  • Addressed Claude findings 1, 2, and 4 in this PR.
  • Addressed Fable operational findings by removing global Foundry FFI, narrowing fs_permissions to ./deployments/, deriving the upgrade script's expected version from the implementation, fixing the MockResolver solhint nit, documenting the supported CreateX salt mode, and verifying DelegationExpired() is selector 0x30d3ba07.
  • Addressed Fable re-review findings by removing process-global vm.setEnv from deploy-script tests, using a harness-local expected-version override, and making unreadable/empty version() a hard upgrade-script failure instead of allowing "unknown" == "unknown".
  • The Fable delegated-intent finding is valid, but intentionally deferred from this B1 patch: binding overload, fee path, indexingKey, and extraData into the signature would further reduce relayer discretion but requires an SDK/signing migration beyond replay protection.
  • EIP-712 domain separator caching is intentionally not implemented here because caching for upgradeable proxies would add storage/initializer complexity for a minor delegated-call gas optimization.
  • Automated storage-layout compatibility remains a future tooling task; a naive forge inspect SP storage-layout gate is not equivalent to the removed Hardhat upgrades validation for this ERC-7201 storage pattern.

Package Tooling

  • package.json now declares packageManager: bun@1.3.13 and version 1.1.4.
  • bun.lock now contains only the OpenZeppelin packages required by Foundry remappings.
  • Removed .npmrc, Prettier config/ignore, hardhat.config.ts, tsconfig.json, and the legacy scripts/*.ts Hardhat deploy/upgrade scripts.
  • Foundry formatting is now the repo formatting standard via bun run fmt, bun run fmt:check, and CI forge fmt --check.

Verification

  • bash -n script/patch-sp-deployment.sh
  • bun run patch:sp -- --help
  • bun run patch:sp -- --list
  • env -u ALCHEMY_API_KEY DEPLOYER=0x0000000000000000000000000000000000001234 bun run patch:sp -- base failed with the expected missing ALCHEMY_API_KEY error.
  • forge test --match-test delegated
  • forge test --match-path test/SPDeployScripts.test.sol
  • zsh -lc 'for i in {1..20}; do forge test --match-path test/SPDeployScripts.test.sol >/tmp/spdeployscripts-race-check.log || { cat /tmp/spdeployscripts-race-check.log; exit 1; }; done; echo "20 focused runs passed"'
  • bun run fmt
  • bun run fmt:check
  • bun run build
  • bun run test
  • git diff --check
  • cast sig "DelegationExpired()"
  • bun install --lockfile-only
  • bun install --frozen-lockfile
  • bun run clean
  • DEPLOYER=0x0000000000000000000000000000000000001234 forge script script/UpgradeSPProxies.s.sol --tc UpgradeSPProxies reverted with the expected ProxySelectionRequired(31337) guard.
  • DEPLOYER=0x0000000000000000000000000000000000001234 forge script script/DeploySPImplementation.s.sol --tc DeploySPImplementation
  • DEPLOYER=0x0000000000000000000000000000000000001234 forge script script/DeploySPProxy.s.sol --tc DeploySPProxy

@boyuanx boyuanx changed the title docs: propose Sign Protocol security hardening plan chore: add Sign Protocol hardening plan and Foundry deploy scripts Jul 1, 2026
@boyuanx boyuanx changed the title chore: add Sign Protocol hardening plan and Foundry deploy scripts chore: add Sign Protocol hardening plan and Foundry/Bun tooling Jul 1, 2026
@boyuanx boyuanx force-pushed the docs/security-hardening-plan branch from f3c0f57 to 180f9d7 Compare July 1, 2026 15:09
@boyuanx boyuanx changed the title chore: add Sign Protocol hardening plan and Foundry/Bun tooling chore: add Sign Protocol Foundry/Bun tooling Jul 1, 2026
@boyuanx boyuanx changed the title chore: add Sign Protocol Foundry/Bun tooling fix: harden delegated signatures and add Foundry tooling Jul 1, 2026
@boyuanx boyuanx merged commit c0d9b6d into main Jul 2, 2026
2 checks passed
@boyuanx boyuanx deleted the docs/security-hardening-plan branch July 2, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant