Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/solidity-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ jobs:
# Example assertion suites live under their own foundry profiles (examples/<name>) and also use the
# Phorge-only `cl.assertion` cheatcode, so they run under `pcl test` here. Add a profile to the
# matrix when its example is ready to be gated on every PR.
# `safe-guard` is a plain-forge integration suite (real Gnosis Safe + CredibleSafeGuard) on its own
# profile because the Safe contracts need the legacy pipeline; pcl runs plain forge tests too.
# `safe-guard` is a plain-forge integration suite under test/protection/safe/integration (real
# Gnosis Safe + CredibleSafeGuard) on its own profile because the Safe contracts need the legacy
# pipeline; pcl runs plain forge tests too.
examples-pcl:
runs-on: ubuntu-latest
strategy:
Expand Down
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@
[submodule "lib/safe-smart-account"]
path = lib/safe-smart-account
url = https://github.com/safe-global/safe-smart-account
[submodule "lib/safe-smart-account-1.3.0"]
path = lib/safe-smart-account-1.3.0
url = https://github.com/safe-global/safe-smart-account
[submodule "lib/safe-smart-account-1.4.0"]
path = lib/safe-smart-account-1.4.0
url = https://github.com/safe-global/safe-smart-account
[submodule "lib/safe-smart-account-1.5.0"]
path = lib/safe-smart-account-1.5.0
url = https://github.com/safe-global/safe-smart-account
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ FOUNDRY_PROFILE=<protocol> forge build
| `nado/` | `ink/assertions` | Nado perpetual clearinghouse |
| `royco/` | `royco-dawn` | Royco kernel accounting and version-neutral tranche state effects |
| `safe/` | `safe-protection-suite` | Safe config lock + tx-shape assertions |
| `safe-guard/` | `safe-guard` | Real Safe integration for `CredibleSafeGuard` |
| `safe-guard/` | `safe-guard` | Deployment and Safe Transaction Builder tooling for `CredibleSafeGuard` |
| `spark/` | `spark` | Spark Vault previews/take accounting and risk-increasing SparkLend oracle guard |
| `symbiotic/` | `symbiotic` | Symbiotic V1 deposit/withdraw/claim/slash accounting; relay and noncausal circuit policy quarantined |
| `tydro/` | `ink/assertions` | Tydro Aave-v3-like operation safety on Ink |
Expand Down
34 changes: 17 additions & 17 deletions examples/safe-guard/README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
# Credible Safe Guard integration
# Credible Safe Guard operations

Real end-to-end tests for `src/protection/safe/CredibleSafeGuard.sol`, run against an actual Gnosis Safe (v1.4.1).
Deployment and Safe Transaction Builder tooling for
`src/protection/safe/CredibleSafeGuard.sol`.

The guard contract and its unit tests live in the root project:
The guard contract, compatibility matrix, and tests live with the Safe protection package:

- `src/protection/safe/CredibleSafeGuard.sol`
- `src/protection/safe/ICredibleRegistry.sol`
- `test/protection/safe/CredibleSafeGuard.t.sol` (unit tests)

These integration tests live under their own profile because the Safe contracts compile with the legacy pipeline (`optimizer = true`, no `via_ir`), while the root profiles use `via_ir`.

## What it covers

- Installing the guard on a real Safe via a signed `execTransaction` → `setGuard`, which exercises Safe's real `GS300` ERC-165 check against the guard.
- A signed Safe transaction executing in a credible block.
- A signed Safe transaction reverting with `NonCredibleBlock` in a non-credible block while the builder set is live.
- The fail-open path: a signed Safe transaction executing once the builder set has been silent past the configured window.
- Registry-unavailability fail-open paths, including reverted and malformed registry responses.
- An end-to-end builder stall-then-recover sequence.
- `src/protection/safe/README.md`
- `test/protection/safe/CredibleSafeGuard.t.sol`
- `test/protection/safe/integration/`

## Deploy the guard

Expand Down Expand Up @@ -88,7 +80,15 @@ Generated files under `safe-guard-output/` are ignored by Git. Always verify the
## Run tests

```sh
FOUNDRY_PROFILE=safe-guard forge test
FOUNDRY_PROFILE=safe-guard pcl test
```

For a focused compatibility-matrix run during development:

```sh
FOUNDRY_PROFILE=safe-guard forge test \
--match-path test/protection/safe/integration/CredibleSafeGuardSafeIntegration.t.sol
```

The Safe contracts come from the `safe-smart-account` submodule under `lib/`, so initialize submodules first (`git submodule update --init --recursive`).
The Safe contracts come from pinned `safe-smart-account` submodules under `lib/`, so initialize
submodules first (`git submodule update --init --recursive`).
200 changes: 0 additions & 200 deletions examples/safe-guard/test/CredibleSafeGuardSafeIntegration.t.sol

This file was deleted.

14 changes: 9 additions & 5 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ out = "out"
libs = ["lib"]
remappings = ["credible-std/=src/"]
via_ir = true
no_match_path = "test/protection/safe/integration/**"

[profile.ci]
src = "src"
Expand All @@ -14,6 +15,7 @@ libs = ["lib"]
remappings = ["credible-std/=src/"]
via_ir = true
optimizer = true
no_match_path = "test/protection/safe/integration/**"

[profile.backtesting]
src = "src"
Expand Down Expand Up @@ -179,12 +181,12 @@ cache_path = "examples/safe/cache"
libs = ["lib"]
remappings = ["credible-std/=src/"]

# Real Gnosis Safe end-to-end tests for src/protection/safe/CredibleSafeGuard.sol.
# Separate profile because the Safe contracts compile with the legacy pipeline
# (optimizer, no via_ir), unlike the via_ir root profiles.
# Real Gnosis Safe compatibility and operational tests for CredibleSafeGuard.
# The suite lives with the protection tests but needs a separate profile because
# the Safe contracts compile with the legacy pipeline (optimizer, no via_ir).
[profile.safe-guard]
src = "examples/safe-guard/src"
test = "examples/safe-guard/test"
src = "src/protection/safe"
test = "test/protection/safe/integration"
script = "examples/safe-guard/script"
out = "examples/safe-guard/out"
cache_path = "examples/safe-guard/cache"
Expand All @@ -193,6 +195,8 @@ remappings = ["credible-std/=src/"]
fs_permissions = [{ access = "read-write", path = "./safe-guard-output" }]
optimizer = true
via_ir = false
# Override the root profiles' exclusion now that this profile targets the integration tree.
no_match_path = "test/protection/safe/integration/.never-match"

[profile.credible-block]
src = "examples/credible-block/src"
Expand Down
1 change: 1 addition & 0 deletions lib/safe-smart-account-1.3.0
1 change: 1 addition & 0 deletions lib/safe-smart-account-1.4.0
1 change: 1 addition & 0 deletions lib/safe-smart-account-1.5.0
45 changes: 45 additions & 0 deletions src/protection/safe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,51 @@ The two `*Assertion` contracts run inside the PhEvm. `CredibleSafeGuard` is a pl

Install it through an owner-authorized Safe self-transaction whose calldata is `setGuard(address(guard))`; a direct owner call is not authorized. Safe then calls `checkTransaction` before each `execTransaction`, and the guard reverts to block the execution. The scripts and Safe Transaction Builder import workflow are documented in [`examples/safe-guard/README.md`](../../../examples/safe-guard/README.md).

### Safe compatibility

The integration suite runs against every guard-capable Safe contract release from 1.3.0 through
1.5.0:

| Safe release | Pinned revision | Singleton / proxy factory | Guard installation validation |
| --- | --- | --- | --- |
| 1.3.0 | `186a21a` | `GnosisSafe` / `GnosisSafeProxyFactory` | Transaction guards supported; no ERC-165 check |
| 1.4.0 | `e870f51` | `Safe` / `SafeProxyFactory` | `GS300` ERC-165 interface check |
| 1.4.1 | `21dc824` (`v1.4.1-3`) | `Safe` / `SafeProxyFactory` | `GS300` ERC-165 interface check |
| 1.5.0 | `dc437e8` | `Safe` / `SafeProxyFactory` | `GS300` ERC-165 interface check |

Safe 1.2.0 is not included because it does not implement transaction guards. The 1.4.1
dependency uses the `v1.4.1-3` source release, whose singleton reports contract version `1.4.1`.
The matrix follows the
[official Safe contract releases](https://github.com/safe-global/safe-smart-account/releases).

Safe 1.3.0 supports installing and running a transaction guard, but its `setGuard` implementation
accepts the address without validating ERC-165 support. Safe 1.4.0 and later reject a nonzero guard
unless it advertises the transaction-guard interface, reverting with `GS300`.
`CredibleSafeGuard` implements that interface and installs successfully on all four versions.

The shared lifecycle suite covers:

- Guard installation through an owner-signed `execTransaction` → `setGuard`, including the real
`GS300` check on Safe 1.4.0 and later.
- Signed transactions executing in credible blocks and reverting with `NonCredibleBlock` in
non-credible blocks while the builder set is live.
- Regular transactions and guard removal remaining blocked at exactly `failOpenBlockThreshold`,
without changing the guard slot or consuming the Safe nonce.
- Guard removal succeeding at `failOpenBlockThreshold + 1`, and before expiry in a credible block.
- Fail-open after builder silence and when registry responses revert or are malformed, including
recovery removal of the guard.
- An end-to-end builder stall-then-recover sequence.

The expiry comparison is strict: fail-open activates only when the gap from the latest credible
block is greater than `failOpenBlockThreshold`. Expiry is only needed for a healthy,
non-credible block; a credible block or an unavailable/malformed registry permits earlier
recovery, preserving the guard's existing policy.

The compatibility and operational tests are under
[`test/protection/safe/integration`](../../../test/protection/safe/integration) and use the
dedicated `safe-guard` Foundry profile because the pinned Safe contracts require the legacy
compiler pipeline (`optimizer = true`, no `via_ir`).

### What It Checks

On every Safe transaction the guard reads the Credible Registry and decides:
Expand Down
Loading
Loading