Skip to content

Commit

Permalink
feat: deployment scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
superical committed Mar 21, 2024
1 parent 9231a0d commit 2e92076
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ export API_KEY_ARBISCAN=
export API_KEY_OPTIMISTIC_ETHERSCAN=

export FOUNDRY_PROFILE="default"

export DEPLOYER_ADDRESS=

# Salts are 11 bytes
export SALT_DOCUMENT_STORE=
export SALT_TRANSFERABLE_DOCUMENT_STORE=
94 changes: 94 additions & 0 deletions script/DeployBase.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.23 <0.9.0;

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

abstract contract DeployBaseScript is Script {
address internal constant FACTORY = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed;
address internal constant DS_IMPL = 0xfC7C29C1f869C6DB5933235C8540Ff4526B26f69;
address internal constant TDS_IMPL = 0x8E9Eb7f019068f42E075f0a985D8A29596C82ED4;

constructor() {
require(getDocumentStoreSalt() != getTransferableDocumentStoreSalt(), "Salts must be different");
}

function getSaltEntropy(string memory entropyName) internal view returns (bytes11) {
bytes11 entropy = bytes11(vm.envOr({name: entropyName, defaultValue: bytes("")}));
require(entropy != bytes11(0), "Salt entropy not set");

return entropy;
}

function getSalt(bytes11 entropy) internal view returns (bytes32 salt) {
require(entropy != bytes11(0), "Salt entropy not be zero");

address deployer = vm.envOr({name: "DEPLOYER_ADDRESS", defaultValue: address(0)});
require(deployer != address(0), "Deployer address not set");

bytes20 deployerBytes = bytes20(deployer);

salt = bytes32(abi.encodePacked(deployerBytes, bytes1(0x00), entropy));
}

function getDocumentStoreSalt() internal view returns (bytes32 salt) {
bytes11 entropy = getSaltEntropy("SALT_DOCUMENT_STORE");
salt = getSalt(entropy);
}

function getTransferableDocumentStoreSalt() internal view returns (bytes32 salt) {
bytes11 entropy = getSaltEntropy("SALT_TRANSFERABLE_DOCUMENT_STORE");
salt = getSalt(entropy);
}

function dsImplExists() internal view returns (bool) {
return _exists(DS_IMPL);
}

function tdsImplExists() internal view returns (bool) {
return _exists(TDS_IMPL);
}

function computeAddr(bytes32 salt) internal view returns (address) {
bytes32 guardedSalt = _hash(bytes32(uint256(uint160(msg.sender))), salt);
(bool ok, bytes memory data) = FACTORY.staticcall(
abi.encodeWithSignature("computeCreate3Address(bytes32)", guardedSalt)
);
require(ok, "Error compute DocumentStoreInitializable address");
return abi.decode(data, (address));
}

function deploy(bytes32 salt, bytes memory initCode) internal returns (address) {
(bool ok, bytes memory data) = FACTORY.call(
abi.encodeWithSignature("deployCreate3(bytes32,bytes)", salt, initCode)
);
require(ok, "Deployment failed");

return abi.decode(data, (address));
}

function clone(address impl, bytes memory initData) internal returns (address) {
(bool ok, bytes memory data) = FACTORY.call(
abi.encodeWithSignature("deployCreate2Clone(address,bytes)", impl, initData)
);
require(ok, "Clone deployment failed");

return abi.decode(data, (address));
}

function _hash(bytes32 a, bytes32 b) internal pure returns (bytes32 hash) {
assembly ("memory-safe") {
mstore(0x00, a)
mstore(0x20, b)
hash := keccak256(0x00, 0x40)
}
}

function _exists(address addr) internal view returns (bool) {
uint256 size;
assembly {
size := extcodesize(addr)
}
return size > 0;
}
}
31 changes: 31 additions & 0 deletions script/DocumentStore.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.23 <0.9.0;

import "./DeployBase.s.sol";
import "../src/DocumentStore.sol";

contract DocumentStoreScript is DeployBaseScript {
function run(string memory name, address admin) public returns (DocumentStore ds) {
_requireParams(name, admin);

console2.log("DocumentStore Name: ", name);
console2.log("DocumentStore Admin: ", admin);

if (dsImplExists()) {
bytes memory initData = abi.encodeWithSignature("initialize(string,address)", name, admin);

vm.broadcast();
address dsAddr = clone(DS_IMPL, initData);

ds = DocumentStore(dsAddr);
} else {
vm.broadcast();
ds = new DocumentStore(name, admin);
}
}

function _requireParams(string memory name, address admin) private pure {
require(bytes(name).length > 0, "Name is required");
require(admin != address(0), "Admin address is required");
}
}
23 changes: 23 additions & 0 deletions script/DocumentStoreInitializable.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.23 <0.9.0;

import "./DeployBase.s.sol";
import "../src/initializables/DocumentStoreInitializable.sol";

contract DocumentStoreInitializableScript is DeployBaseScript {
function run() public returns (DocumentStoreInitializable documentStore) {
require(!dsImplExists(), "DocumentStoreInitializable already exists");

bytes memory initCode = abi.encodePacked(type(DocumentStoreInitializable).creationCode);

bytes32 dsSalt = getDocumentStoreSalt();

address computedAddr = computeAddr(dsSalt);
require(computedAddr == DS_IMPL, "Bad deployment address");

vm.broadcast();
address dsAddr = deploy(dsSalt, initCode);

documentStore = DocumentStoreInitializable(dsAddr);
}
}
33 changes: 33 additions & 0 deletions script/OwnableDocumentStore.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.23 <0.9.0;

import "./DeployBase.s.sol";
import "../src/OwnableDocumentStore.sol";

contract OwnableDocumentStoreScript is DeployBaseScript {
function run(string memory name, string memory symbol, address admin) public returns (OwnableDocumentStore ds) {
_requireParams(name, symbol, admin);

console2.log("OwnableDocumentStore Name: ", name);
console2.log("OwnableDocumentStore Symbol: ", symbol);
console2.log("OwnableDocumentStore Admin: ", admin);

if (tdsImplExists()) {
bytes memory initData = abi.encodeWithSignature("initialize(string,string,address)", name, symbol, admin);

vm.broadcast();
address dsAddr = clone(TDS_IMPL, initData);

ds = OwnableDocumentStore(dsAddr);
} else {
vm.broadcast();
ds = new OwnableDocumentStore(name, symbol, admin);
}
}

function _requireParams(string memory name, string memory symbol, address admin) private pure {
require(bytes(name).length > 0, "Name is required");
require(bytes(symbol).length > 0, "Symbol is required");
require(admin != address(0), "Admin address is required");
}
}
23 changes: 23 additions & 0 deletions script/OwnableDocumentStoreInitializable.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.23 <0.9.0;

import "./DeployBase.s.sol";
import "../src/initializables/OwnableDocumentStoreInitializable.sol";

contract OwnableDocumentStoreInitializableScript is DeployBaseScript {
function run() public returns (OwnableDocumentStoreInitializable documentStore) {
require(!dsImplExists(), "OwnableDocumentStoreInitializable already exists");

bytes memory initCode = abi.encodePacked(type(OwnableDocumentStoreInitializable).creationCode);

bytes32 dsSalt = getTransferableDocumentStoreSalt();

address computedAddr = computeAddr(dsSalt);
require(computedAddr == TDS_IMPL, "Bad deployment address");

vm.broadcast();
address dsAddr = deploy(dsSalt, initCode);

documentStore = OwnableDocumentStoreInitializable(dsAddr);
}
}

0 comments on commit 2e92076

Please sign in to comment.