Skip to content

Commit 72f74e3

Browse files
authored
feat: add deployment scripts for CoW Swap trading infrastructure (#7)
This PR adds the deployment infrastructure for the CoW Swap trading system components. ### Key Changes - Add `ScriptUtils` for deployment address management - Add sequential deployment scripts for all contracts - Add deployments tracking ### Implementation Details - `ScriptUtils`: - Manages contract addresses across different chains - Provides consistent address reading/writing interface - Handles deployment validation - Deployment Flow: 1. Deploy `TokenAllowlist` 2. Deploy `CoWSwapGuard` 3. Deploy `TradingModule` mastercopy via singleton factory (Gnosis Chain) 4. Deploy `TradingModule` proxy via module proxy factory (Gnosis Chain) 5. Enable module on target Safe (Gnosis Chain)
1 parent 8a45aed commit 72f74e3

10 files changed

+290
-6
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ yarn.lock
4141
# Broadcasts
4242
!broadcast
4343
broadcast/*
44-
broadcast/*/31337/
44+
broadcast/*/31337/
45+
broadcast*
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"deployedChains": [1, 100, 8453, 31337, 42161, 11155111],
3+
"chains": {
4+
"1": {
5+
"allowlist": "0x0000000000000000000000000000000000000000",
6+
"guard": "0x0000000000000000000000000000000000000000",
7+
"tradingModuleMasterCopy": "0x0000000000000000000000000000000000000000",
8+
"tradingModuleProxy": "0x0000000000000000000000000000000000000000"
9+
},
10+
"100": {
11+
"allowlist": "0x0000000000000000000000000000000000000000",
12+
"guard": "0x0000000000000000000000000000000000000000",
13+
"tradingModuleMasterCopy": "0x0000000000000000000000000000000000000000",
14+
"tradingModuleProxy": "0x0000000000000000000000000000000000000000"
15+
},
16+
"8453": {
17+
"allowlist": "0x0000000000000000000000000000000000000000",
18+
"guard": "0x0000000000000000000000000000000000000000",
19+
"tradingModuleMasterCopy": "0x0000000000000000000000000000000000000000",
20+
"tradingModuleProxy": "0x0000000000000000000000000000000000000000"
21+
},
22+
"31337": {
23+
"allowlist": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
24+
"guard": "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512",
25+
"tradingModuleMasterCopy": "0x0000000000000000000000000000000000000000",
26+
"tradingModuleProxy": "0x0000000000000000000000000000000000000000"
27+
},
28+
"42161": {
29+
"allowlist": "0x0000000000000000000000000000000000000000",
30+
"guard": "0x0000000000000000000000000000000000000000",
31+
"tradingModuleMasterCopy": "0x0000000000000000000000000000000000000000",
32+
"tradingModuleProxy": "0x0000000000000000000000000000000000000000"
33+
},
34+
"11155111": {
35+
"allowlist": "0x0000000000000000000000000000000000000000",
36+
"guard": "0x0000000000000000000000000000000000000000",
37+
"tradingModuleMasterCopy": "0x0000000000000000000000000000000000000000",
38+
"tradingModuleProxy": "0x0000000000000000000000000000000000000000"
39+
}
40+
}
41+
}

smart-contract-infra/foundry.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
[profile.default]
22
auto_detect_solc = false
3-
block_timestamp = 1_738_368_000 # Feb 1, 2025 at 00:00 GMT
3+
block_timestamp = 1_738_368_000
44
fuzz = { runs = 1_000 }
55
optimizer = true
66
optimizer_runs = 10_000
77
out = "out"
88
script = "script"
99
solc = "0.8.28"
1010
src = "src"
11+
fs_permissions = [{ access = "read-write", path = "./deployments" }]
1112

1213
[profile.ci]
1314
fuzz = { runs = 10_000 }
1415
verbosity = 4
1516

16-
[etherscan]
17-
mainnet = { key = "${API_KEY_ETHERSCAN}" }
18-
1917
[fmt]
2018
bracket_spacing = true
2119
int_types = "long"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import { console2 } from "forge-std/Script.sol";
5+
import { ScriptUtils } from "script/ScriptUtils.sol";
6+
import { TokenAllowlist } from "src/TokenAllowlist.sol";
7+
8+
// With verification:
9+
/*
10+
forge script script/01_Deploy_Allowlist.s.sol \
11+
--rpc-url gnosis \
12+
--private-key $PRIVATE_KEY \
13+
--verify --etherscan-api-key $ETHERSCAN_API_KEY \
14+
--broadcast -vvvv
15+
*/
16+
17+
// Without verification:
18+
/*
19+
forge script script/01_Deploy_Allowlist.s.sol \
20+
--rpc-url gnosis \
21+
--private-key $PRIVATE_KEY \
22+
--broadcast -vvvv
23+
*/
24+
25+
contract DeployAllowlist is ScriptUtils {
26+
function run() external {
27+
uint256 pk = vm.envUint("PRIVATE_KEY");
28+
29+
vm.startBroadcast(pk);
30+
31+
address allowlist = address(new TokenAllowlist());
32+
console2.log("Allowlist deployed to:", allowlist);
33+
_writeDeploymentAddress(allowlist, ".allowlist");
34+
35+
vm.stopBroadcast();
36+
}
37+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import { console2 } from "forge-std/Script.sol";
5+
import { ScriptUtils } from "script/ScriptUtils.sol";
6+
import { CoWSwapGuard } from "src/CoWSwapGuard.sol";
7+
8+
// With verification:
9+
/*
10+
forge script script/02_Deploy_CoWSwapGuard.s.sol \
11+
--rpc-url gnosis \
12+
--private-key $PRIVATE_KEY \
13+
--verify --etherscan-api-key $ETHERSCAN_API_KEY \
14+
--broadcast -vvvv
15+
*/
16+
17+
// Without verification:
18+
/*
19+
forge script script/02_Deploy_CoWSwapGuard.s.sol \
20+
--rpc-url gnosis \
21+
--private-key $PRIVATE_KEY \
22+
--broadcast -vvvv
23+
*/
24+
25+
contract DeployGuard is ScriptUtils {
26+
function run() external {
27+
uint256 pk = vm.envUint("PRIVATE_KEY");
28+
29+
vm.startBroadcast(pk);
30+
31+
address allowlist = _getDeploymentAddress(".allowlist");
32+
address guard = address(new CoWSwapGuard(allowlist));
33+
console2.log("CoWSwapGuard deployed to:", guard);
34+
_writeDeploymentAddress(guard, ".guard");
35+
36+
vm.stopBroadcast();
37+
}
38+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import { console2 } from "forge-std/Script.sol";
5+
import { ScriptUtils } from "script/ScriptUtils.sol";
6+
import { TradingModule } from "src/TradingModule.sol";
7+
8+
interface ISingletonFactory {
9+
function deploy(bytes memory, bytes32) external returns (address payable createdContract);
10+
}
11+
12+
// With verification:
13+
/*
14+
forge script script/03_Deploy_MasterCopy.s.sol \
15+
--rpc-url gnosis \
16+
--private-key $PRIVATE_KEY \
17+
--verify --etherscan-api-key $ETHERSCAN_API_KEY \
18+
--broadcast -vvvv
19+
*/
20+
21+
// Without verification:
22+
/*
23+
forge script script/03_Deploy_MasterCopy.s.sol \
24+
--rpc-url gnosis \
25+
--private-key $PRIVATE_KEY \
26+
--broadcast -vvvv
27+
*/
28+
29+
contract DeployMasterCopy is ScriptUtils {
30+
address internal constant SINGLETON_FACTORY = 0xce0042B868300000d44A59004Da54A005ffdcf9f;
31+
address internal constant ZERO_ADDRESS = address(0);
32+
33+
function run() external {
34+
uint256 pk = vm.envUint("PRIVATE_KEY");
35+
36+
bytes memory creationCode = type(TradingModule).creationCode;
37+
bytes memory constructorArgs = abi.encode(ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS);
38+
bytes memory initCode = abi.encodePacked(creationCode, constructorArgs);
39+
40+
bytes32 salt = keccak256(abi.encodePacked("TradingModuleV1"));
41+
42+
vm.startBroadcast(pk);
43+
44+
ISingletonFactory factory = ISingletonFactory(SINGLETON_FACTORY);
45+
address tradingModuleMasterCopy = factory.deploy(initCode, salt);
46+
47+
console2.log("TradingModule MasterCopy deployed to:", tradingModuleMasterCopy);
48+
_writeDeploymentAddress(tradingModuleMasterCopy, ".tradingModuleMasterCopy");
49+
50+
vm.stopBroadcast();
51+
}
52+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import { console2 } from "forge-std/Script.sol";
5+
import { ScriptUtils } from "script/ScriptUtils.sol";
6+
import { TradingModule } from "src/TradingModule.sol";
7+
8+
interface IModuleProxyFactory {
9+
function deployModule(address, bytes memory, uint256) external returns (address);
10+
}
11+
12+
// With verification:
13+
/*
14+
forge script script/04_Deploy_TradingModuleProxy.s.sol \
15+
--rpc-url gnosis \
16+
--private-key $PRIVATE_KEY \
17+
--verify --etherscan-api-key $ETHERSCAN_API_KEY \
18+
--broadcast -vvvv
19+
*/
20+
21+
// Without verification:
22+
/*
23+
forge script script/04_Deploy_TradingModuleProxy.s.sol \
24+
--rpc-url gnosis \
25+
--private-key $PRIVATE_KEY \
26+
--broadcast -vvvv
27+
*/
28+
contract DeployTradingModuleProxy is ScriptUtils {
29+
address internal constant MODULE_PROXY_FACTORY = 0x000000000000aDdB49795b0f9bA5BC298cDda236;
30+
address internal constant SAFE = 0x5aFE3855358E112B5647B952709E6165e1c1eEEe;
31+
32+
function run() external {
33+
uint256 pk = vm.envUint("PRIVATE_KEY");
34+
35+
address masterCopy = _getDeploymentAddress(".tradingModuleMasterCopy");
36+
37+
bytes memory initData = abi.encodeCall(TradingModule.setUp, (abi.encode(SAFE, SAFE, SAFE)));
38+
bytes32 salt = keccak256(abi.encodePacked("TradingModuleProxyV1"));
39+
40+
vm.startBroadcast(pk);
41+
42+
address proxy = IModuleProxyFactory(MODULE_PROXY_FACTORY).deployModule(masterCopy, initData, uint256(salt));
43+
console2.log("TradingModule Proxy deployed to:", proxy);
44+
_writeDeploymentAddress(proxy, ".tradingModuleProxy");
45+
46+
vm.stopBroadcast();
47+
}
48+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import { console2 } from "forge-std/Script.sol";
5+
import { ScriptUtils } from "script/ScriptUtils.sol";
6+
7+
interface ISafe {
8+
function enableModule(address module) external;
9+
}
10+
11+
// With verification:
12+
/*
13+
forge script script/05_Enable_TradingModule.s.sol \
14+
--rpc-url gnosis \
15+
--private-key $PRIVATE_KEY \
16+
--broadcast -vvvv
17+
*/
18+
19+
// Without verification:
20+
/*
21+
forge script script/05_Enable_TradingModule.s.sol \
22+
--rpc-url gnosis \
23+
--private-key $PRIVATE_KEY \
24+
--broadcast -vvvv
25+
*/
26+
contract EnableTradingModule is ScriptUtils {
27+
address internal constant SAFE = 0x5aFE3855358E112B5647B952709E6165e1c1eEEe;
28+
29+
function run() external {
30+
uint256 pk = vm.envUint("PRIVATE_KEY");
31+
32+
address tradingModuleProxy = _getDeploymentAddress(".tradingModuleProxy");
33+
34+
vm.startBroadcast(pk);
35+
36+
ISafe(SAFE).enableModule(tradingModuleProxy);
37+
console2.log("Enabled TradingModule:", tradingModuleProxy);
38+
39+
vm.stopBroadcast();
40+
}
41+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.28;
3+
4+
import { Script } from "forge-std/Script.sol";
5+
import { stdJson } from "forge-std/StdJson.sol";
6+
7+
contract ScriptUtils is Script {
8+
using stdJson for string;
9+
10+
error AddressNotFound(string key);
11+
12+
function _writeDeploymentAddress(address addr, string memory key) internal {
13+
string memory root = vm.projectRoot();
14+
string memory path = string.concat(root, "/deployments/contracts.json");
15+
string memory ds = vm.toString(addr);
16+
vm.writeJson(ds, path, string.concat(".chains.", vm.toString(block.chainid), key));
17+
}
18+
19+
function _getDeploymentAddress(string memory key) internal view returns (address) {
20+
string memory root = vm.projectRoot();
21+
string memory path = string.concat(root, "/deployments/contracts.json");
22+
string memory json = vm.readFile(path);
23+
string memory addressPath = string.concat(".chains.", vm.toString(block.chainid), key);
24+
address addr = json.readAddress(addressPath);
25+
if (addr == address(0)) revert AddressNotFound(key);
26+
return addr;
27+
}
28+
}

smart-contract-infra/src/TradingModule.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { GPv2Signing } from "cowprotocol/contracts/src/contracts/mixins/GPv2Sign
1010
import { IAvatar } from "@gnosis-guild/zodiac/contracts/interfaces/IAvatar.sol";
1111
import { IGuard } from "@gnosis-guild/zodiac/contracts/interfaces/IGuard.sol";
1212

13-
abstract contract TradingModule is Module, Guardable {
13+
contract TradingModule is Module, Guardable {
1414
using GPv2Order for bytes;
1515

1616
/// @notice Emitted when an order was successfully set

0 commit comments

Comments
 (0)