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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FORK_RPC_URL=
BASE_RPC_URL=
COW_HISTORICAL_TX_HASHES=
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
env:
FORK_RPC_URL: ${{ secrets.FORK_RPC_URL }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # action v6.0.2
with:
Expand Down
15 changes: 11 additions & 4 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ slither:

# Run tests
test:
forge test -vvv --show-progress --gas-snapshot-check true
forge test -vvv --show-progress

# Print coverage summary
coverage-summary:
Expand All @@ -51,9 +51,17 @@ coverage-lcov:

# Fail if the minimum of all four coverage metrics (lines/statements/branches/funcs) on the `Total` row is below `COVERAGE_MIN` (default `100`)
coverage-check:
@{{JUST}} coverage-summary > coverage.txt
cat coverage.txt
# Fields on the `| Total | ... |` row are: $4=lines, $7=statements, $10=branches, $13=funcs (whitespace-split, `%` stripped)
@tmp_dir="$(mktemp -d)"; \
snapshot_patch="$tmp_dir/snapshots.patch"; \
git diff --binary -- snapshots > "$snapshot_patch"; \
cleanup() { git restore --worktree snapshots; if [ -s "$snapshot_patch" ]; then git apply "$snapshot_patch"; fi; rm -rf "$tmp_dir"; rm -f coverage.txt; }; \
trap cleanup EXIT; \
if ! {{JUST}} coverage-summary > coverage.txt 2>&1; then \
cat coverage.txt; \
exit 1; \
fi; \
cat coverage.txt; \
Comment thread
kaze-cow marked this conversation as resolved.
awk -v threshold={{COVERAGE_MIN}} '\
BEGIN { labels[4]="lines"; labels[7]="statements"; labels[10]="branches"; labels[13]="funcs"; min=100; below="" } \
/^\| Total/ { \
Expand All @@ -69,7 +77,6 @@ coverage-check:
if (!found) { print "Failed to extract coverage percentage."; exit 1 } \
if (min < threshold) { printf "\nMetrics below minimum threshold of %s%%:\n%s\n", threshold, below; exit 1 } \
}' coverage.txt
rm coverage.txt

# Generate gas snapshots
snapshot:
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ If specific features are needed (like PUSH0 in 0.8.20 for gas optimizations or t
just test
```

#### Replaying Your Own Historical Transactions

The fork test `test_fork_historicalTransaction_directVsDelegated_userSuppliedTxHashes` lets you replay your own batch transactions through the delegate.

Set:

- `FORK_RPC_URL` to the RPC URL you want Foundry to fork from.
- `COW_HISTORICAL_TX_HASHES` to a comma-separated list of transaction hashes.

The supplied transaction hashes just need to exist on that network, and the RPC must support the historical state needed by `vm.rollFork(txHash)`.

Example:

```shell
FORK_RPC_URL=<your_rpc_url> \
COW_HISTORICAL_TX_HASHES=0xabc...,0xdef... \
just test --match-test test_fork_historicalTransaction_directVsDelegated_userSuppliedTxHashes
```

### Format

```shell
Expand Down Expand Up @@ -151,4 +170,4 @@ The following operations need to be performed after this repository has been cre
- [ ] Set up [Local tooling](#local-tooling) so Solhint and Slither use the pinned project versions
- [ ] Update the project details in `dev/package.json`, including `name` and `description`
- [ ] Make sure you use the [latest version of Solidity](https://github.com/argotorg/solidity/releases) by updating the `solc` version in `foundry.toml`
- [ ] Once all entries in this list are checked, delete this section from the readme
- [ ] Once all entries in this list are checked, delete this section from the readme
3 changes: 2 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.34" # See latest release at: https://github.com/argotorg/solidity/releases
isolate = true

[fmt]
sort_imports = true
Expand All @@ -13,4 +14,4 @@ wrap_comments = true
deny = "warnings" # Why not always: sometimes you just want to code and see what comes out
verbosity = 3 # Outputs stack traces for failed tests.
fuzz.seed = "0"
fuzz.runs = 10_000
fuzz.runs = 10_000
4 changes: 4 additions & 0 deletions snapshots/Solver7702DelegateForkTest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"historical tx - swap and bridge order - delegated call": "949703",
"historical tx - swap and bridge order - direct call": "945548"
}
11 changes: 11 additions & 0 deletions snapshots/Solver7702DelegateTest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"approved caller empty target payload - success - forwards": "24507",
"approved caller short calldata - success - receives ETH": "21760",
"approved caller short calldata - success - returns empty": "21760",
"approved caller target empty revert - reverts - bubbles empty data": "24861",
"approved caller target payload - success - forwards": "31258",
"approved caller target return data - success - bubbles return data": "25069",
"approved caller target revert data - reverts - bubbles non-empty data": "25738",
"unauthorized caller - success - receives ETH": "21232",
"unauthorized caller no value - reverts - unauthorized": "21930"
}
1 change: 1 addition & 0 deletions src/Solver7702Delegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ contract Solver7702Delegate {
}

/// @notice Fallback function to handle calls to the delegate
/// @dev Expected calldata format is `bytes20(target) || targetCalldata`.
fallback() external payable {
// Possibly short circuit by recognizing one of the approved callers
if (
Expand Down
5 changes: 5 additions & 0 deletions test/.solhint.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
{
"rules": {
"func-name-mixedcase": "off",
"gas-strict-inequalities": "off",
"gas-small-strings": "off",
"avoid-low-level-calls": "off",
"max-states-count": "off",
"no-empty-blocks": "off",
"use-natspec": "off"
}
}
26 changes: 26 additions & 0 deletions test/BaseTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.34;
Comment thread
igorroncevic marked this conversation as resolved.

import {Test} from "forge-std/Test.sol";

/// @notice Shared setup and helpers for Solver7702Delegate tests.
abstract contract BaseTest is Test {
/// @notice Shared approved callers.
address[5] internal approvedCallers;

/// @notice Creates the default approved callers.
function setUp() public virtual {
approvedCallers = [
makeAddr("APPROVED_CALLER_0"),
makeAddr("APPROVED_CALLER_1"),
makeAddr("APPROVED_CALLER_2"),
makeAddr("APPROVED_CALLER_3"),
makeAddr("APPROVED_CALLER_4")
];
}

/// @notice Encodes the delegate fallback calldata as a 20-byte target followed by payload.
function _packedCalldata(address target, bytes memory payload) internal pure returns (bytes memory) {
return abi.encodePacked(target, payload);
}
}
Loading
Loading