Skip to content

Commit

Permalink
Code Jar Deployments
Browse files Browse the repository at this point in the history
This patch switches our core Factory to be `CodeJarFactory` that deploys `CodeJar` deterministically via `scripts/deploy-code-jar.sh`. After that, you're expected to pass in `CODE_JAR` to the deployment script which now depends on using that same Code Jar for deployments.

The key benefit here is that we can re-use that same CodeJar access multiple network deployments. In fact, if we get a consistent CodeJar address on all chains (e.g. via running the deployment script on all standard EVM chains), then we can use whatever key to deploy any new Quark Factory contracts and they will always end up at the same address on all chains. This should make multi-chain deployments a breeze!

Plus, everything _should_ be verifiable, etc, on Etherscan now, too.
  • Loading branch information
hayesgm committed Mar 2, 2024
1 parent a3e63b7 commit ef0a8e7
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 31 deletions.
41 changes: 41 additions & 0 deletions script/DeployCodeJarFactory.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;

import "forge-std/Script.sol";
import "forge-std/console.sol";

import {CodeJar} from "codejar/src/CodeJar.sol";
import {CodeJarFactory} from "codejar/src/CodeJarFactory.sol";

// Deploy with:
// $ set -a && source .env && ./script/deploy.sh --broadcast

// Required ENV vars:
// RPC_URL
// DEPLOYER_PK

// Optional ENV vars:
// ETHERSCAN_KEY

contract DeployCodeJarFactory is Script {
CodeJarFactory codeJarFactory;
CodeJar codeJar;

function run() public {
address deployer = vm.addr(vm.envUint("DEPLOYER_PK"));

vm.startBroadcast(deployer);

console.log("=============================================================");

console.log("Deploying Code Jar Factory");
codeJarFactory = new CodeJarFactory();
codeJar = codeJarFactory.codeJar();
console.log("Code Jar Factory Deployed:", address(codeJarFactory));
console.log("Code Jar Deployed:", address(codeJar));

console.log("=============================================================");

vm.stopBroadcast();
}
}
13 changes: 6 additions & 7 deletions script/DeployQuarkWalletFactory.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import {Multicall} from "quark-core-scripts/src/Multicall.sol";
// Required ENV vars:
// RPC_URL
// DEPLOYER_PK
// CODE_JAR

// Optional ENV vars:
// ETHERSCAN_KEY

contract DeployQuarkWalletFactory is Script {
CodeJar codeJar;
QuarkWalletProxyFactory quarkWalletProxyFactory;
BatchExecutor batchExecutor;
Ethcall ethcall;
Expand All @@ -34,17 +36,16 @@ contract DeployQuarkWalletFactory is Script {

function run() public {
address deployer = vm.addr(vm.envUint("DEPLOYER_PK"));
codeJar = CodeJar(vm.addr(vm.envUint("CODE_JAR")));

vm.startBroadcast(deployer);

console.log("=============================================================");

console.log("Deploying Quark Factory");
quarkFactory = new QuarkFactory();
quarkFactory = new QuarkFactory(codeJar);
console.log("Quark Factory Deployed:", address(quarkFactory));

console.log("Deploying Quark Contracts via Quark Factory");
quarkFactory.deployQuarkContracts();
console.log("Code Jar Deployed:", address(quarkFactory.codeJar()));
console.log("Quark State Manager Deployed:", address(quarkFactory.quarkStateManager()));
console.log("Quark Wallet Implementation Deployed:", address(quarkFactory.quarkWalletImpl()));
Expand All @@ -53,12 +54,10 @@ contract DeployQuarkWalletFactory is Script {

console.log("Deploying Core Scripts");

CodeJar codeJar = QuarkWallet(payable(quarkFactory.quarkWalletProxyFactory().walletImplementation())).codeJar();

ethcall = Ethcall(codeJar.saveCode(vm.getCode(string.concat("out/", "Ethcall.sol/Ethcall.json"))));
ethcall = Ethcall(payable(codeJar.saveCode(abi.encodePacked(type(Ethcall).creationCode))));
console.log("Ethcall Deployed:", address(ethcall));

multicall = Multicall(codeJar.saveCode(vm.getCode(string.concat("out/", "Multicall.sol/Multicall.json"))));
multicall = Multicall(payable(codeJar.saveCode(abi.encodePacked(type(Multicall).creationCode))));
console.log("Multicall Deployed:", address(multicall));

console.log("=============================================================");
Expand Down
28 changes: 28 additions & 0 deletions script/deploy-code-jar.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

set -exo pipefail

if [ -n "$RPC_URL" ]; then
rpc_args="--rpc-url $RPC_URL"
else
rpc_args=""
fi

if [ -n "$DEPLOYER_PK" ]; then
wallet_args="--private-key $DEPLOYER_PK"
else
wallet_args="--unlocked"
fi

if [ -n "$ETHERSCAN_KEY" ]; then
etherscan_args="--verify --etherscan-api-key $ETHERSCAN_KEY"
else
etherscan_args=""
fi

forge script --via-ir \
$rpc_args \
$wallet_args \
$etherscan_args \
$@ \
script/DeployCodeJarFactory.s.sol:DeployCodeJarFactory
17 changes: 17 additions & 0 deletions src/codejar/src/CodeJarFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.23;

import {CodeJar} from "codejar/src/CodeJar.sol";

/**
* @title Code Jar Factory
* @notice A factory for deploying Code Jar to a content-determinstic address
* @author Compound Labs, Inc.
*/
contract CodeJarFactory {
CodeJar public immutable codeJar;

constructor() {
codeJar = new CodeJar{salt: 0}();
}
}
15 changes: 8 additions & 7 deletions src/quark-factory/src/QuarkFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@ import {BatchExecutor} from "quark-core/src/periphery/BatchExecutor.sol";
* @author Compound Labs, Inc.
*/
contract QuarkFactory {
CodeJar public codeJar;
CodeJar public immutable codeJar;
QuarkWallet public quarkWalletImpl;
QuarkWalletProxyFactory public quarkWalletProxyFactory;
QuarkStateManager public quarkStateManager;
BatchExecutor public batchExecutor;

constructor() {}
constructor(CodeJar codeJar_) {
codeJar = codeJar_;
}

function deployQuarkContracts() external {
codeJar = new CodeJar{salt: 0}();
quarkStateManager = new QuarkStateManager{salt: 0}();
quarkWalletImpl = new QuarkWallet{salt: 0}(codeJar, quarkStateManager);
quarkWalletProxyFactory = new QuarkWalletProxyFactory{salt: 0}(address(quarkWalletImpl));
batchExecutor = new BatchExecutor{salt: 0}();
quarkStateManager = QuarkStateManager(payable(codeJar.saveCode(abi.encodePacked(type(QuarkStateManager).creationCode))));
quarkWalletImpl = QuarkWallet(payable(codeJar.saveCode(abi.encodePacked(type(QuarkWallet).creationCode, abi.encode(codeJar, quarkStateManager)))));
quarkWalletProxyFactory = QuarkWalletProxyFactory(payable(codeJar.saveCode(abi.encodePacked(type(QuarkWalletProxyFactory).creationCode, abi.encode(quarkWalletImpl)))));
batchExecutor = BatchExecutor(payable(codeJar.saveCode(abi.encodePacked(type(BatchExecutor).creationCode))));
}
}
44 changes: 27 additions & 17 deletions test/quark-factory/QuarkFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,31 @@ import "forge-std/StdMath.sol";

import {QuarkFactory} from "quark-factory/src/QuarkFactory.sol";
import {CodeJar} from "codejar/src/CodeJar.sol";
import {CodeJarFactory} from "codejar/src/CodeJarFactory.sol";
import {QuarkWallet} from "quark-core/src/QuarkWallet.sol";
import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol";
import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol";
import {BatchExecutor} from "quark-core/src/periphery/BatchExecutor.sol";

contract QuarkFactoryTest is Test {
CodeJarFactory public codeJarFactory;
CodeJar public codeJar;
QuarkFactory public factory;

function setUp() public {
factory = new QuarkFactory();
codeJarFactory = new CodeJarFactory();
codeJar = codeJarFactory.codeJar();
factory = new QuarkFactory(codeJar);
}

function testQuarkFactoryDeployToDeterministicAddresses() public {
vm.pauseGasMetering();
address expectedCodeJarAddress =
getCreate2AddressHelper(address(factory), bytes32(0), type(CodeJar).creationCode);
getCreate2AddressHelper(address(codeJarFactory), bytes32(0), type(CodeJar).creationCode);
address expectedQuarkStateManagerAddress =
getCreate2AddressHelper(address(factory), bytes32(0), type(QuarkStateManager).creationCode);
getCreate2AddressHelper(address(codeJar), bytes32(0), type(QuarkStateManager).creationCode);
address expectedQuarkWalletImplAddress = getCreate2AddressHelper(
address(factory),
address(codeJar),
bytes32(0),
abi.encodePacked(
type(QuarkWallet).creationCode,
Expand All @@ -36,12 +41,12 @@ contract QuarkFactoryTest is Test {
)
);
address expectedQuarkWalletProxyFactoryAddress = getCreate2AddressHelper(
address(factory),
address(codeJar),
bytes32(0),
abi.encodePacked(type(QuarkWalletProxyFactory).creationCode, abi.encode(expectedQuarkWalletImplAddress))
);
address expectedBatchExecutorAddress =
getCreate2AddressHelper(address(factory), bytes32(0), type(BatchExecutor).creationCode);
getCreate2AddressHelper(address(codeJar), bytes32(0), type(BatchExecutor).creationCode);

vm.resumeGasMetering();
factory.deployQuarkContracts();
Expand All @@ -55,12 +60,12 @@ contract QuarkFactoryTest is Test {
function testQuarkFactoryDeployTwice() public {
vm.pauseGasMetering();
address expectedCodeJarAddress =
getCreate2AddressHelper(address(factory), bytes32(0), abi.encodePacked(type(CodeJar).creationCode));
getCreate2AddressHelper(address(codeJarFactory), bytes32(0), abi.encodePacked(type(CodeJar).creationCode));
address expectedQuarkStateManagerAddress = getCreate2AddressHelper(
address(factory), bytes32(0), abi.encodePacked(type(QuarkStateManager).creationCode)
address(codeJar), bytes32(0), abi.encodePacked(type(QuarkStateManager).creationCode)
);
address expectedQuarkWalletImplAddress = getCreate2AddressHelper(
address(factory),
address(codeJar),
bytes32(0),
abi.encodePacked(
type(QuarkWallet).creationCode,
Expand All @@ -69,13 +74,13 @@ contract QuarkFactoryTest is Test {
)
);
address expectedQuarkWalletProxyFactoryAddress = getCreate2AddressHelper(
address(factory),
address(codeJar),
bytes32(0),
abi.encodePacked(type(QuarkWalletProxyFactory).creationCode, abi.encode(expectedQuarkWalletImplAddress))
);

address expectedBatchExecutorAddress =
getCreate2AddressHelper(address(factory), bytes32(0), type(BatchExecutor).creationCode);
getCreate2AddressHelper(address(codeJar), bytes32(0), type(BatchExecutor).creationCode);

vm.resumeGasMetering();
factory.deployQuarkContracts();
Expand All @@ -85,18 +90,23 @@ contract QuarkFactoryTest is Test {
assertEq(address(factory.quarkStateManager()), expectedQuarkStateManagerAddress);
assertEq(address(factory.batchExecutor()), expectedBatchExecutorAddress);

vm.expectRevert();
// This doesn't need to revert. It's mostly a no-op
factory.deployQuarkContracts();
assertEq(address(factory.codeJar()), expectedCodeJarAddress);
assertEq(address(factory.quarkWalletImpl()), expectedQuarkWalletImplAddress);
assertEq(address(factory.quarkWalletProxyFactory()), expectedQuarkWalletProxyFactoryAddress);
assertEq(address(factory.quarkStateManager()), expectedQuarkStateManagerAddress);
assertEq(address(factory.batchExecutor()), expectedBatchExecutorAddress);
}

function testInvariantAddressesBetweenNonces() public {
vm.pauseGasMetering();
address expectedCodeJarAddress =
getCreate2AddressHelper(address(factory), bytes32(0), type(CodeJar).creationCode);
getCreate2AddressHelper(address(codeJarFactory), bytes32(0), type(CodeJar).creationCode);
address expectedQuarkStateManagerAddress =
getCreate2AddressHelper(address(factory), bytes32(0), type(QuarkStateManager).creationCode);
getCreate2AddressHelper(address(codeJar), bytes32(0), type(QuarkStateManager).creationCode);
address expectedQuarkWalletImplAddress = getCreate2AddressHelper(
address(factory),
address(codeJar),
bytes32(0),
abi.encodePacked(
type(QuarkWallet).creationCode,
Expand All @@ -105,13 +115,13 @@ contract QuarkFactoryTest is Test {
)
);
address expectedQuarkWalletProxyFactoryAddress = getCreate2AddressHelper(
address(factory),
address(codeJar),
bytes32(0),
abi.encodePacked(type(QuarkWalletProxyFactory).creationCode, abi.encode(expectedQuarkWalletImplAddress))
);

address expectedBatchExecutorAddress =
getCreate2AddressHelper(address(factory), bytes32(0), type(BatchExecutor).creationCode);
getCreate2AddressHelper(address(codeJar), bytes32(0), type(BatchExecutor).creationCode);

// Set a different nonce on the account, assuming some unaware ations done to the deployer eoa cuasing nonces to change
vm.setNonce(address(this), 20);
Expand Down

0 comments on commit ef0a8e7

Please sign in to comment.