Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
skip = ["CredibleSafeGuardSafeIntegration", "CredibleSafeGuardScripts"]

[profile.ci]
src = "src"
Expand All @@ -14,6 +15,7 @@ libs = ["lib"]
remappings = ["credible-std/=src/"]
via_ir = true
optimizer = true
skip = ["CredibleSafeGuardSafeIntegration", "CredibleSafeGuardScripts"]

[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
# Compile the integration files skipped by the root via-IR profiles.
skip = []

[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