Skip to content

Commit abdc204

Browse files
feat: added support for zksync work arounds (#24)
1 parent 61f46bd commit abdc204

10 files changed

+310
-17
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ docs/
1818
*report.md
1919
reports/*.*
2020

21+
22+
# zkSync
23+
zkout/

README.md

+77-8
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@ A repo to get the most recent deployment from a given environment in foundry. Th
44

55
It will look through your `broadcast` folder at your most recent deployment.
66

7+
## Features
8+
- Get the most recent deployment of a contract in foundry
9+
- Checking if your on a zkSync based chain
10+
711
- [foundry-devops](#foundry-devops)
12+
- [Features](#features)
813
- [Getting Started](#getting-started)
914
- [Requirements](#requirements)
1015
- [Installation](#installation)
11-
- [Usage](#usage)
16+
- [Usage - Getting the most recent deployment](#usage---getting-the-most-recent-deployment)
17+
- [Usage - zkSync Checker](#usage---zksync-checker)
1218
- [Contributing](#contributing)
1319
- [Testing](#testing)
1420

21+
1522
# Getting Started
1623

1724
## Requirements
@@ -25,7 +32,7 @@ It will look through your `broadcast` folder at your most recent deployment.
2532

2633
## Installation
2734

28-
```
35+
```bash
2936
forge install Cyfrin/foundry-devops --no-commit
3037
```
3138

@@ -37,15 +44,18 @@ git rm -rf lib/forge-std
3744
rm -rf lib/forge-std
3845
```
3946
```
40-
forge install foundry-rs/[email protected].0 --no-commit
47+
forge install foundry-rs/[email protected].2 --no-commit
4148
```
4249

43-
## Usage
50+
## Usage - Getting the most recent deployment
4451

4552
1. Update your `foundry.toml` to have read permissions on the `broadcast` folder.
4653

47-
```
48-
fs_permissions = [{ access = "read", path = "./broadcast" }]
54+
```toml
55+
fs_permissions = [
56+
{ access = "read", path = "./broadcast" },
57+
{ access = "read", path = "./reports" },
58+
]
4959
```
5060

5161
2. Import the package, and call `DevOpsTools.get_most_recent_deployment("MyContract", chainid);`
@@ -65,6 +75,57 @@ function interactWithPreviouslyDeployedContracts() public {
6575
}
6676
```
6777

78+
## Usage - zkSync Checker
79+
80+
### Prerequisites
81+
- [foundry-zksync](https://github.com/matter-labs/foundry-zksync)
82+
- You'll know you did it right if you can run `foundryup-zksync --help` and you see a response like:
83+
```
84+
The installer for Foundry-zksync.
85+
86+
Update or revert to a specific Foundry-zksync version with ease.
87+
.
88+
.
89+
.
90+
```
91+
92+
### Usage - ZkSyncChecker
93+
94+
In your contract, you can import and inherit the abstract contract `ZkSyncChecker` to check if you are on a zkSync based chain. And add the `skipZkSync` modifier to any function you want to skip if you are on a zkSync based chain.
95+
96+
It will check both the precompiles or the `chainid` to determine if you are on a zkSync based chain.
97+
98+
```javascript
99+
import {ZkSyncChecker} from "lib/foundry-devops/src/ZkSyncChecker.sol";
100+
101+
contract MyContract is ZkSyncChecker {
102+
103+
function doStuff() skipZkSync {
104+
```
105+
106+
### ZkSyncChecker modifiers:
107+
- `skipZkSync`: Skips the function if you are on a zkSync based chain.
108+
- `onlyZkSync`: Only allows the function if you are on a zkSync based chain.
109+
110+
### ZkSync Checker Functions:
111+
- `isZkSyncChain()`: Returns true if you are on a zkSync based chain.
112+
- `isOnZkSyncPrecompiles()`: Returns true if you are on a zkSync based chain using the precompiles.
113+
- `isOnZkSyncChainId()`: Returns true if you are on a zkSync based chain using the chainid.
114+
115+
### Usage - FoundryZkSyncChecker
116+
117+
In your contract, you can import and inherit the abstract contract `FoundryZkSyncChecker` to check if you are on the `foundry-zksync` fork of `foundry`.
118+
119+
> !Important: Functions and modifiers in `FoundryZkSyncChecker` are only available if you run `foundry-zksync` with the `--zksync` flag.
120+
121+
```javascript
122+
import {FoundryZkSyncChecker} from "lib/foundry-devops/src/FoundryZkSyncChecker.sol";
123+
124+
contract MyContract is FoundryZkSyncChecker {
125+
126+
function doStuff() onlyFoundryZkSync {
127+
```
128+
68129
# Contributing
69130
70131
PRs are welcome!
@@ -77,6 +138,14 @@ forge install
77138
78139
## Testing
79140
141+
For testing on vanilla foundry, run:
142+
143+
```bash
144+
make test
80145
```
81-
forge test
82-
```
146+
147+
For testing with `foundry-zksync`, run:
148+
149+
```bash
150+
make test-zksync
151+
```

foundry.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,11 @@ fs_permissions = [
66
{ access = "read", path = "./broadcast" },
77
{ access = "read", path = "./reports" },
88
]
9+
ffi = true
910

10-
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
11+
12+
[rpc_endpoints]
13+
mainnet = "${MAINNET_RPC_URL}"
14+
zksync = "${ZKSYNC_RPC_URL}"
15+
16+
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

makefile

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: update anvil deploy
1+
.PHONY: update anvil deploy test test-zksync
22

33
DEFAULT_ANVIL_KEY := 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
44

@@ -8,4 +8,8 @@ anvil:; anvil -m 'test test test test test test test test test test test junk'
88

99
deploy:; forge script script/DeployStuff.s.sol:DeployStuff --broadcast --rpc-url http://localhost:8545 --private-key $(DEFAULT_ANVIL_KEY) -vvvv
1010

11-
interact:; forge script script/InteractWithStuff.s.sol:InteractWithStuff --broadcast --rpc-url http://localhost:8545 --private-key $(DEFAULT_ANVIL_KEY) -vvvv
11+
interact:; forge script script/InteractWithStuff.s.sol:InteractWithStuff --broadcast --rpc-url http://localhost:8545 --private-key $(DEFAULT_ANVIL_KEY) -vvvv
12+
13+
test-zksync :; foundryup-zksync && forge test --zksync
14+
15+
test :; foundryup && forge test

src/FoundryZkSyncChecker.sol

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.18;
3+
4+
import {Script, console2} from "forge-std/Script.sol";
5+
6+
abstract contract FoundryZkSyncChecker is Script {
7+
/**
8+
* @notice returns the current version of foundry.
9+
*/
10+
function is_foundry_zksync() internal returns (bool) {
11+
string[] memory command = new string[](2);
12+
command[0] = "bash";
13+
command[1] = "src/is_foundry_zksync.sh";
14+
bytes memory retData = vm.ffi(command);
15+
bool isFoundryZkSync = uint256(bytes32(retData)) > 0;
16+
return isFoundryZkSync;
17+
}
18+
19+
modifier onlyFoundryZkSync() {
20+
if (!is_foundry_zksync()) {
21+
console2.log("Only foundry-zksync works with this function");
22+
} else {
23+
_;
24+
}
25+
}
26+
27+
modifier onlyVanillaFoundry() {
28+
if (is_foundry_zksync()) {
29+
console2.log("Only foundry works with this function");
30+
} else {
31+
_;
32+
}
33+
}
34+
}

src/ZkSyncChainChecker.sol

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// SPDX-License-Identifier: AGPL-3.0-or-later
2+
pragma solidity ^0.8.18;
3+
4+
import {console2} from "forge-std/console2.sol";
5+
6+
abstract contract ZkSyncChainChecker {
7+
uint256 zkSyncMainnetChainId = 324;
8+
uint256 zkSyncSepoliaChainId = 300;
9+
uint256 zkSyncInMemoryNodeChainId = 260;
10+
11+
function isOnZkSyncChainId() public view returns (bool) {
12+
// We can make a "dummy" check by looking at the chainId, but this won't work for when working with foundry
13+
return block.chainid == zkSyncMainnetChainId || block.chainid == zkSyncSepoliaChainId
14+
|| block.chainid == zkSyncInMemoryNodeChainId;
15+
}
16+
17+
function isOnZkSyncPrecompiles() public returns (bool isZkSync) {
18+
// https://docs.zksync.io/build/developer-reference/differences-with-ethereum.html#precompiles
19+
// As of writing, at least 0x03, 0x04, 0x05, and 0x08 precompiles are not supported on zkSync
20+
// So, we can call them to check if we are on zkSync or not
21+
// This test may fail in the future if these precompiles become supported on zkSync
22+
uint256 value = 1;
23+
address ripemd = address(uint160(3));
24+
address identity = address(uint160(4));
25+
address modexp = address(uint160(5));
26+
address ecPairing = address(uint160(8));
27+
28+
address[4] memory targets = [ripemd, identity, modexp, ecPairing];
29+
30+
for (uint256 i = 0; i < targets.length; i++) {
31+
bool success;
32+
address target = targets[i];
33+
assembly {
34+
success := call(gas(), target, value, 0, 0, 0, 0)
35+
}
36+
if (!success) {
37+
isZkSync = true;
38+
return isZkSync;
39+
}
40+
}
41+
return isZkSync;
42+
}
43+
44+
function isZkSyncChain() public returns (bool isZkSync) {
45+
if (isOnZkSyncChainId()) {
46+
return isZkSync;
47+
}
48+
return isOnZkSyncPrecompiles();
49+
}
50+
51+
modifier skipZkSync() {
52+
if (isZkSyncChain()) {
53+
console2.log("Skipping test because we are on zkSync");
54+
return;
55+
} else {
56+
_;
57+
}
58+
}
59+
60+
modifier onlyZkSync() {
61+
if (!isZkSyncChain()) {
62+
console2.log("Skipping test because we are not on zkSync");
63+
return;
64+
} else {
65+
_;
66+
}
67+
}
68+
}

src/is_foundry_zksync.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
3+
output=$(forge --version)
4+
5+
if [[ $output == forge\ 0.2.0* ]]; then
6+
echo 0x00 # This is false
7+
else
8+
echo 0x01 # This is true
9+
fi

test/DevOpsToolsTest.t.sol

+8-6
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,36 @@ pragma solidity >=0.8.0 <0.9.0;
33

44
import {Test} from "forge-std/Test.sol";
55
import {DevOpsTools} from "../src/DevOpsTools.sol";
6+
import {ZkSyncChainChecker} from "../src/ZkSyncChainChecker.sol";
7+
import {FoundryZkSyncChecker} from "../src/FoundryZkSyncChecker.sol";
68

7-
contract DevOpsToolsTest is Test {
9+
contract DevOpsToolsTest is Test, ZkSyncChainChecker, FoundryZkSyncChecker {
810
string public constant SEARCH_PATH = "broadcast";
911

10-
function testGetMostRecentlyDeployedContract() public view {
12+
function testGetMostRecentlyDeployedContract() public skipZkSync onlyVanillaFoundry {
1113
string memory contractName = "Stuff";
1214
uint256 chainId = 31337;
1315
address expectedAddress = 0x5FbDB2315678afecb367f032d93F642f64180aa3;
1416
address mostRecentDeployment = DevOpsTools.get_most_recent_deployment(contractName, chainId, SEARCH_PATH);
1517
assertEq(mostRecentDeployment, expectedAddress);
1618
}
1719

18-
function testGetMostRecentlyDeployedEvenWhenMultipleAreDeployed() public view {
20+
function testGetMostRecentlyDeployedEvenWhenMultipleAreDeployed() public skipZkSync onlyVanillaFoundry {
1921
string memory contractName = "FundMe";
2022
uint256 chainId = 1234;
2123
address expectedAddress = 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9;
2224
address mostRecentDeployment = DevOpsTools.get_most_recent_deployment(contractName, chainId, SEARCH_PATH);
2325
assertEq(mostRecentDeployment, expectedAddress);
2426
}
2527

26-
function testExpectRevertIfNoRun() public {
28+
function testExpectRevertIfNoRun() public onlyVanillaFoundry {
2729
string memory contractName = "FundMe";
2830
uint256 chainId = 9999;
2931
vm.expectRevert("No deployment artifacts were found for specified chain");
3032
DevOpsTools.get_most_recent_deployment(contractName, chainId, SEARCH_PATH);
3133
}
3234

33-
function testExpectRevertIfNoDeployment() public {
35+
function testExpectRevertIfNoDeployment() public skipZkSync onlyVanillaFoundry {
3436
string memory contractName = "MissingContract";
3537
uint256 chainId = 1234;
3638
vm.expectRevert("No contract deployed");
@@ -39,7 +41,7 @@ contract DevOpsToolsTest is Test {
3941

4042
// All other tests use what appear to be legacy broadcast files
4143
// This one uses the newer type with no rpc property
42-
function testNonLegacyBroadcast() public view {
44+
function testNonLegacyBroadcast() public skipZkSync onlyVanillaFoundry {
4345
string memory contractName = "NewStuff";
4446
uint256 chainId = 31337;
4547
address expectedAddress = 0x5FbDB2315678afecb367f032d93F642f64180aa3;
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: MIt
2+
pragma solidity ^0.8.18;
3+
4+
import {ZkSyncChainChecker} from "src/ZkSyncChainChecker.sol";
5+
import {FoundryZkSyncChecker} from "src/FoundryZkSyncChecker.sol";
6+
import {Test, console2} from "forge-std/Test.sol";
7+
import {Vm} from "forge-std/Vm.sol";
8+
9+
contract Checker is ZkSyncChainChecker {}
10+
11+
/**
12+
* @title ZkSyncChainCheckerLocalTest
13+
* @author Patrick Collins
14+
* @notice We need to test the local zkSync edition separately, because we cannot do
15+
* vm.selectFork on foundry-zksync
16+
*/
17+
contract ZkSyncChainCheckerLocalTest is Test, FoundryZkSyncChecker {
18+
Checker checkerLocal;
19+
uint256 AMOUNT = 1e18;
20+
21+
function setUp() public {
22+
checkerLocal = new Checker();
23+
vm.deal(address(checkerLocal), AMOUNT);
24+
}
25+
26+
/*//////////////////////////////////////////////////////////////
27+
BY CHAINID
28+
//////////////////////////////////////////////////////////////*/
29+
function testIsOnZkSyncChainByChainId_local() public onlyVanillaFoundry {
30+
assertEq(checkerLocal.isOnZkSyncChainId(), false);
31+
}
32+
33+
/*//////////////////////////////////////////////////////////////
34+
BY PRECOMPILES
35+
//////////////////////////////////////////////////////////////*/
36+
function testIsOnZkSyncByPrecompiles_local() public onlyFoundryZkSync {
37+
assertEq(checkerLocal.isOnZkSyncPrecompiles(), true);
38+
}
39+
}

0 commit comments

Comments
 (0)