mirror of
https://github.com/luxfhe/research-ocp-fhe.git
synced 2026-10-30 17:09:36 +00:00
412 lines
19 KiB
Solidity
412 lines
19 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import "forge-std/Script.sol";
|
|
import "forge-std/console.sol";
|
|
import { MockFacet, MockFacetV2 } from "../test/mocks/MockFacet.sol";
|
|
import { CapTableFactory } from "@core/CapTableFactory.sol";
|
|
import { CapTable } from "@core/CapTable.sol";
|
|
import { DiamondCutFacet } from "diamond-3-hardhat/facets/DiamondCutFacet.sol";
|
|
import { IssuerFacet } from "@facets/IssuerFacet.sol";
|
|
import { DiamondLoupeFacet } from "diamond-3-hardhat/facets/DiamondLoupeFacet.sol";
|
|
import { IDiamondCut } from "diamond-3-hardhat/interfaces/IDiamondCut.sol";
|
|
import { StakeholderFacet } from "@facets/StakeholderFacet.sol";
|
|
import { StockClassFacet } from "@facets/StockClassFacet.sol";
|
|
import { StockFacet } from "@facets/StockFacet.sol";
|
|
import { ConvertiblesFacet } from "@facets/ConvertiblesFacet.sol";
|
|
import { EquityCompensationFacet } from "@facets/EquityCompensationFacet.sol";
|
|
import { StockPlanFacet } from "@facets/StockPlanFacet.sol";
|
|
import { WarrantFacet } from "@facets/WarrantFacet.sol";
|
|
import { StakeholderNFTFacet } from "@facets/StakeholderNFTFacet.sol";
|
|
import { AccessControl } from "@libraries/AccessControl.sol";
|
|
import { AccessControlFacet } from "@facets/AccessControlFacet.sol";
|
|
import { Strings } from "openzeppelin-contracts/contracts/utils/Strings.sol";
|
|
|
|
library LibDeployment {
|
|
uint256 constant FACET_COUNT = 11; // Number of enum values FacetType
|
|
|
|
enum FacetType {
|
|
DiamondLoupe,
|
|
Issuer,
|
|
Stakeholder,
|
|
StockClass,
|
|
Stock,
|
|
Convertibles,
|
|
EquityCompensation,
|
|
StockPlan,
|
|
Warrant,
|
|
StakeholderNFT,
|
|
AccessControl,
|
|
MockFacet,
|
|
MockFacetV2
|
|
}
|
|
|
|
struct FacetCutInfo {
|
|
string name;
|
|
bytes4[] selectors;
|
|
}
|
|
|
|
function getFacetCutInfo(FacetType facetType) internal pure returns (FacetCutInfo memory info) {
|
|
if (facetType == FacetType.DiamondLoupe) {
|
|
bytes4[] memory selectors = new bytes4[](5);
|
|
selectors[0] = DiamondLoupeFacet.facets.selector;
|
|
selectors[1] = DiamondLoupeFacet.facetFunctionSelectors.selector;
|
|
selectors[2] = DiamondLoupeFacet.facetAddresses.selector;
|
|
selectors[3] = DiamondLoupeFacet.facetAddress.selector;
|
|
selectors[4] = DiamondLoupeFacet.supportsInterface.selector;
|
|
return FacetCutInfo({ name: "DiamondLoupeFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.Issuer) {
|
|
bytes4[] memory selectors = new bytes4[](2);
|
|
selectors[0] = IssuerFacet.initializeIssuer.selector;
|
|
selectors[1] = IssuerFacet.adjustIssuerAuthorizedShares.selector;
|
|
return FacetCutInfo({ name: "IssuerFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.Stakeholder) {
|
|
bytes4[] memory selectors = new bytes4[](3);
|
|
selectors[0] = StakeholderFacet.createStakeholder.selector;
|
|
selectors[1] = StakeholderFacet.getStakeholderPositions.selector;
|
|
selectors[2] = StakeholderFacet.linkStakeholderAddress.selector;
|
|
return FacetCutInfo({ name: "StakeholderFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.StockClass) {
|
|
bytes4[] memory selectors = new bytes4[](2);
|
|
selectors[0] = StockClassFacet.createStockClass.selector;
|
|
selectors[1] = StockClassFacet.adjustAuthorizedShares.selector;
|
|
return FacetCutInfo({ name: "StockClassFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.Stock) {
|
|
bytes4[] memory selectors = new bytes4[](5);
|
|
selectors[0] = StockFacet.issueStock.selector;
|
|
selectors[1] = StockFacet.getStockPosition.selector;
|
|
selectors[2] = StockFacet.transferStock.selector;
|
|
selectors[3] = StockFacet.getStakeholderSecurities.selector;
|
|
selectors[4] = StockFacet.cancelStock.selector;
|
|
return FacetCutInfo({ name: "StockFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.Convertibles) {
|
|
bytes4[] memory selectors = new bytes4[](2);
|
|
selectors[0] = ConvertiblesFacet.issueConvertible.selector;
|
|
selectors[1] = ConvertiblesFacet.getConvertiblePosition.selector;
|
|
return FacetCutInfo({ name: "ConvertiblesFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.EquityCompensation) {
|
|
bytes4[] memory selectors = new bytes4[](3);
|
|
selectors[0] = EquityCompensationFacet.issueEquityCompensation.selector;
|
|
selectors[1] = EquityCompensationFacet.getPosition.selector;
|
|
selectors[2] = EquityCompensationFacet.exerciseEquityCompensation.selector;
|
|
return FacetCutInfo({ name: "EquityCompensationFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.StockPlan) {
|
|
bytes4[] memory selectors = new bytes4[](2);
|
|
selectors[0] = StockPlanFacet.createStockPlan.selector;
|
|
selectors[1] = StockPlanFacet.adjustStockPlanPool.selector;
|
|
return FacetCutInfo({ name: "StockPlanFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.Warrant) {
|
|
bytes4[] memory selectors = new bytes4[](2);
|
|
selectors[0] = WarrantFacet.issueWarrant.selector;
|
|
selectors[1] = WarrantFacet.getWarrantPosition.selector;
|
|
return FacetCutInfo({ name: "WarrantFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.StakeholderNFT) {
|
|
bytes4[] memory selectors = new bytes4[](2);
|
|
selectors[0] = StakeholderNFTFacet.mint.selector;
|
|
selectors[1] = StakeholderNFTFacet.tokenURI.selector;
|
|
return FacetCutInfo({ name: "StakeholderNFTFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.AccessControl) {
|
|
bytes4[] memory selectors = new bytes4[](8);
|
|
selectors[0] = AccessControlFacet.grantRole.selector;
|
|
selectors[1] = AccessControlFacet.revokeRole.selector;
|
|
selectors[2] = AccessControlFacet.hasRole.selector;
|
|
selectors[3] = AccessControlFacet.initializeAccessControl.selector;
|
|
selectors[4] = AccessControlFacet.transferAdmin.selector;
|
|
selectors[5] = AccessControlFacet.acceptAdmin.selector;
|
|
selectors[6] = AccessControlFacet.getAdmin.selector;
|
|
selectors[7] = AccessControlFacet.getPendingAdmin.selector;
|
|
return FacetCutInfo({ name: "AccessControlFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.MockFacet) {
|
|
bytes4[] memory selectors = new bytes4[](1);
|
|
selectors[0] = MockFacet.getValuePlusOne.selector;
|
|
return FacetCutInfo({ name: "MockFacet", selectors: selectors });
|
|
}
|
|
if (facetType == FacetType.MockFacetV2) {
|
|
bytes4[] memory selectors = new bytes4[](1);
|
|
selectors[0] = MockFacetV2.getValuePlusTwo.selector;
|
|
return FacetCutInfo({ name: "MockFacetV2", selectors: selectors });
|
|
}
|
|
revert("Unknown facet type");
|
|
}
|
|
|
|
function getFacetTypeFromSelector(bytes4 selector) internal pure returns (FacetType) {
|
|
if (selector == DiamondLoupeFacet.facets.selector) return FacetType.DiamondLoupe;
|
|
if (selector == IssuerFacet.initializeIssuer.selector) return FacetType.Issuer;
|
|
if (selector == StakeholderFacet.createStakeholder.selector) return FacetType.Stakeholder;
|
|
if (selector == StockClassFacet.createStockClass.selector) return FacetType.StockClass;
|
|
if (selector == StockFacet.issueStock.selector) return FacetType.Stock;
|
|
if (selector == ConvertiblesFacet.issueConvertible.selector) return FacetType.Convertibles;
|
|
if (selector == EquityCompensationFacet.issueEquityCompensation.selector) return FacetType.EquityCompensation;
|
|
if (selector == StockPlanFacet.createStockPlan.selector) return FacetType.StockPlan;
|
|
if (selector == WarrantFacet.issueWarrant.selector) return FacetType.Warrant;
|
|
if (selector == StakeholderNFTFacet.mint.selector) return FacetType.StakeholderNFT;
|
|
if (selector == AccessControlFacet.grantRole.selector) return FacetType.AccessControl;
|
|
if (selector == MockFacet.getValuePlusOne.selector) return FacetType.MockFacet;
|
|
if (selector == MockFacetV2.getValuePlusTwo.selector) return FacetType.MockFacetV2;
|
|
if (selector == StockFacet.cancelStock.selector) return FacetType.Stock;
|
|
revert("Unknown selector");
|
|
}
|
|
|
|
function _deployedHandler(string memory envName, address addr) internal view returns (address) {
|
|
console.log(string.concat(envName, "=", Strings.toHexString(addr)));
|
|
return addr;
|
|
}
|
|
|
|
function deployFacet(FacetType facetType) internal returns (address) {
|
|
if (facetType == FacetType.DiamondLoupe) {
|
|
return _deployedHandler("DIAMOND_LOUPE_FACET", address(new DiamondLoupeFacet()));
|
|
}
|
|
if (facetType == FacetType.Issuer) {
|
|
return _deployedHandler("ISSUER_FACET", address(new IssuerFacet()));
|
|
}
|
|
if (facetType == FacetType.Stakeholder) {
|
|
return _deployedHandler("STAKEHOLDER_FACET", address(new StakeholderFacet()));
|
|
}
|
|
if (facetType == FacetType.StockClass) {
|
|
return _deployedHandler("STOCK_CLASS_FACET", address(new StockClassFacet()));
|
|
}
|
|
if (facetType == FacetType.Stock) {
|
|
return _deployedHandler("STOCK_FACET", address(new StockFacet()));
|
|
}
|
|
if (facetType == FacetType.Convertibles) {
|
|
return _deployedHandler("CONVERTIBLES_FACET", address(new ConvertiblesFacet()));
|
|
}
|
|
if (facetType == FacetType.EquityCompensation) {
|
|
return _deployedHandler("EQUITY_COMPENSATION_FACET", address(new EquityCompensationFacet()));
|
|
}
|
|
if (facetType == FacetType.StockPlan) {
|
|
return _deployedHandler("STOCK_PLAN_FACET", address(new StockPlanFacet()));
|
|
}
|
|
if (facetType == FacetType.Warrant) {
|
|
return _deployedHandler("WARRANT_FACET", address(new WarrantFacet()));
|
|
}
|
|
if (facetType == FacetType.StakeholderNFT) {
|
|
return _deployedHandler("STAKEHOLDER_NFT_FACET", address(new StakeholderNFTFacet()));
|
|
}
|
|
if (facetType == FacetType.AccessControl) {
|
|
return _deployedHandler("ACCESS_CONTROL_FACET", address(new AccessControlFacet()));
|
|
}
|
|
if (facetType == FacetType.MockFacet) {
|
|
return _deployedHandler("MOCK_FACET", address(new MockFacet()));
|
|
}
|
|
if (facetType == FacetType.MockFacetV2) {
|
|
return _deployedHandler("MOCK_FACET_V2", address(new MockFacetV2()));
|
|
}
|
|
revert("Unknown facet type");
|
|
}
|
|
|
|
function deployInitialFacets(address owner) internal returns (address) {
|
|
console.log("\n\nDeploying facets...");
|
|
console.log("owner(this): ", address(this));
|
|
|
|
// Deploy all facets
|
|
IDiamondCut.FacetCut[] memory cuts = new IDiamondCut.FacetCut[](FACET_COUNT); // Change from FACET_COUNT to actual number of cuts
|
|
|
|
// ------------------- Diamond Loupe Facet -------------------
|
|
cuts[0] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.DiamondLoupe),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.DiamondLoupe).selectors
|
|
});
|
|
|
|
// ------------------- Issuer Facet -------------------
|
|
cuts[1] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.Issuer),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Issuer).selectors
|
|
});
|
|
|
|
// ------------------- Stakeholder Facet -------------------
|
|
cuts[2] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.Stakeholder),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Stakeholder).selectors
|
|
});
|
|
|
|
// ------------------- Stock Class Facet -------------------
|
|
cuts[3] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.StockClass),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.StockClass).selectors
|
|
});
|
|
|
|
// ------------------- Stock Facet -------------------
|
|
cuts[4] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.Stock),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Stock).selectors
|
|
});
|
|
|
|
// ------------------- Convertibles Facet -------------------
|
|
cuts[5] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.Convertibles),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Convertibles).selectors
|
|
});
|
|
cuts[5].functionSelectors[0] = ConvertiblesFacet.issueConvertible.selector;
|
|
cuts[5].functionSelectors[1] = ConvertiblesFacet.getConvertiblePosition.selector;
|
|
|
|
// ------------------- Equity Compensation Facet -------------------
|
|
cuts[6] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.EquityCompensation),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.EquityCompensation).selectors
|
|
});
|
|
|
|
// ------------------- Stock Plan Facet -------------------
|
|
cuts[7] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.StockPlan),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.StockPlan).selectors
|
|
});
|
|
|
|
// ------------------- Warrant Facet -------------------
|
|
cuts[8] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.Warrant),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.Warrant).selectors
|
|
});
|
|
|
|
// ------------------- Stakeholder NFT Facet -------------------
|
|
cuts[9] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.StakeholderNFT),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.StakeholderNFT).selectors
|
|
});
|
|
|
|
// ------------------- Access Control Facet -------------------
|
|
cuts[10] = IDiamondCut.FacetCut({
|
|
facetAddress: LibDeployment.deployFacet(FacetType.AccessControl),
|
|
action: IDiamondCut.FacetCutAction.Add,
|
|
functionSelectors: LibDeployment.getFacetCutInfo(FacetType.AccessControl).selectors
|
|
});
|
|
|
|
// ------------------- Diamond Cut Facet -------------------
|
|
address diamondCutFacet = address(new DiamondCutFacet());
|
|
_deployedHandler("DIAMOND_CUT_FACET", diamondCutFacet);
|
|
|
|
// Create reference diamond
|
|
CapTable referenceDiamond = new CapTable(owner, diamondCutFacet);
|
|
|
|
// Perform the cuts
|
|
DiamondCutFacet(address(referenceDiamond)).diamondCut(cuts, address(0), "");
|
|
console.log("Cuts completed for reference diamond at:", address(referenceDiamond));
|
|
return address(referenceDiamond);
|
|
}
|
|
}
|
|
|
|
contract DeployFactoryScript is Script {
|
|
// runs locally on anvil
|
|
function run() external {
|
|
console.log("Deploying factory on anvil");
|
|
string memory privateKeyStr = vm.envString("PRIVATE_KEY");
|
|
if (bytes(privateKeyStr).length == 0) {
|
|
revert("Missing PRIVATE_KEY in .env");
|
|
}
|
|
// Remove any whitespace and ensure 0x prefix
|
|
if (bytes(privateKeyStr)[0] != "0" || bytes(privateKeyStr)[1] != "x") {
|
|
revert("PRIVATE_KEY must start with 0x");
|
|
}
|
|
uint256 deployerPrivateKey = vm.parseUint(privateKeyStr);
|
|
address deployerWallet = vm.addr(deployerPrivateKey);
|
|
|
|
vm.startBroadcast(deployerWallet);
|
|
|
|
// Try to get addresses from env
|
|
address referenceDiamond = vm.envOr("REFERENCE_DIAMOND", address(0));
|
|
console.log("deployerWallet: ", deployerWallet);
|
|
|
|
// If no reference diamond is provided, deploy new facets
|
|
if (referenceDiamond == address(0)) {
|
|
console.log("\nDeploying new reference diamond and facets...");
|
|
referenceDiamond = LibDeployment.deployInitialFacets(deployerWallet);
|
|
}
|
|
|
|
console.log("------- New Facet Addresses (Add to .env) -------");
|
|
console.log("REFERENCE_DIAMOND=", referenceDiamond);
|
|
console.log("---------------------------------");
|
|
|
|
// Deploy factory with facet addresses
|
|
CapTableFactory factory = new CapTableFactory(referenceDiamond);
|
|
console.log("\n------- Factory Address (add to .env) -------");
|
|
console.log("FACTORY_ADDRESS=", address(factory));
|
|
console.log("-------------------------------------------------");
|
|
|
|
vm.stopBroadcast();
|
|
}
|
|
|
|
function runProduction() external {
|
|
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
|
if (deployerPrivateKey == 0) {
|
|
revert("Missing PRIVATE_KEY in .env");
|
|
}
|
|
address deployer = vm.addr(deployerPrivateKey);
|
|
console.log("Deploying DiamondCapTable system to Base Sepolia");
|
|
|
|
vm.startBroadcast(deployer);
|
|
|
|
// Try to get addresses from env
|
|
address referenceDiamond = vm.envOr("REFERENCE_DIAMOND", address(0));
|
|
|
|
// Deploy new facets if addresses not in env
|
|
if (referenceDiamond == address(0)) {
|
|
revert("Missing REFERENCE_DIAMOND in .env");
|
|
}
|
|
// Deploy factory with facet addresses
|
|
CapTableFactory factory = new CapTableFactory(referenceDiamond);
|
|
|
|
console.log("\nCapTableFactory deployed at:", address(factory));
|
|
|
|
vm.stopPrank();
|
|
// console.log("Diamond admin after accepting:", AccessControlFacet(diamond).getAdmin());
|
|
// Verify factory is no longer admin
|
|
// console.log(
|
|
// "Factory is admin:", AccessControlFacet(diamond).hasRole(AccessControl.DEFAULT_ADMIN_ROLE, address(factory))
|
|
// );
|
|
vm.stopPrank();
|
|
vm.stopBroadcast();
|
|
}
|
|
|
|
function createCapTable() external {
|
|
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
|
if (deployerPrivateKey == 0) {
|
|
revert("Missing PRIVATE_KEY in .env");
|
|
}
|
|
// address deployerWallet = vm.addr(deployerPrivateKey);
|
|
console.log("Creating a new cap table");
|
|
|
|
// Get addresses from env
|
|
address referenceDiamond = vm.envOr("REFERENCE_DIAMOND", address(0));
|
|
address factoryAddress = vm.envOr("FACTORY_ADDRESS", address(0));
|
|
|
|
if (referenceDiamond == address(0)) {
|
|
revert("Missing REFERENCE_DIAMOND in .env");
|
|
}
|
|
if (factoryAddress == address(0)) {
|
|
revert("Missing FACTORY_ADDRESS in .env");
|
|
}
|
|
|
|
vm.startBroadcast(deployerPrivateKey);
|
|
|
|
// Use existing factory
|
|
CapTableFactory factory = CapTableFactory(factoryAddress);
|
|
// Pass EIN and initial authorized shares (1 billion with 18 decimals)
|
|
address capTable = factory.createCapTable("1234567890", 1_000_000_000_000_000_000_000_000_000);
|
|
|
|
console.log("\nNew CapTable created at:", capTable);
|
|
|
|
vm.stopBroadcast();
|
|
}
|
|
}
|