From f08a780a98cd9652e4db7a9d7c4c54f469f54c88 Mon Sep 17 00:00:00 2001 From: detoo Date: Sun, 26 Apr 2026 16:35:42 -0700 Subject: [PATCH 01/20] chore: devops-related comments --- script/libs/DeploymentConstants.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/script/libs/DeploymentConstants.sol b/script/libs/DeploymentConstants.sol index 2b51050..52ebc7e 100644 --- a/script/libs/DeploymentConstants.sol +++ b/script/libs/DeploymentConstants.sol @@ -38,10 +38,10 @@ library DeploymentConstants { metalexSafe: 0x68Ab3F79622cBe74C9683aA54D7E1BBdCAE8003C, auth: 0x033012a1eDA6e2E00D12CD37c5b63B9440ef5E01, cyberCorpFactory: 0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2, - issuanceManagerFactory: 0xbbD386D237f3b407E6511A52488850b1Da0cCad2, + issuanceManagerFactory: 0xbbD386D237f3b407E6511A52488850b1Da0cCad2, // different from all other chains cyberCorpSingleFactory: 0xBE0D3D13AA07501beAC9b72dE9e9292E66C7A5C4, dealManagerFactory: 0x3982b078f2ac306219c9540Ebc908360a960C251, - roundManagerFactory: 0x9E2A3a07711Ce4b5A2F4D62a5c8f8B5307Af9C34, + roundManagerFactory: 0x9E2A3a07711Ce4b5A2F4D62a5c8f8B5307Af9C34, // different from all other chains cyberAgreementRegistry: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, uriBuilder: 0x5500c095ea7dE6F8a5E15949e24B80604cc670A3, lexchexAuth: 0xeAdeaD5C4A6747D4959489742c143bCDb95a01c2, @@ -56,10 +56,10 @@ library DeploymentConstants { metalexSafe: 0x68Ab3F79622cBe74C9683aA54D7E1BBdCAE8003C, auth: 0x033012a1eDA6e2E00D12CD37c5b63B9440ef5E01, cyberCorpFactory: 0x51413048f3Dfc4516e95BC8e249341B1D53B6cB2, - issuanceManagerFactory: 0xD353972D7955F421d94d0eA8c42c88c417F7155A, + issuanceManagerFactory: 0xD353972D7955F421d94d0eA8c42c88c417F7155A, // except base-sepolia cyberCorpSingleFactory: 0xBE0D3D13AA07501beAC9b72dE9e9292E66C7A5C4, dealManagerFactory: 0x3982b078f2ac306219c9540Ebc908360a960C251, - roundManagerFactory: 0xc9d5d0DeDD124f9351E5880469f25AB41869aeb9, + roundManagerFactory: 0xc9d5d0DeDD124f9351E5880469f25AB41869aeb9, // except base-sepolia cyberAgreementRegistry: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, uriBuilder: 0x5500c095ea7dE6F8a5E15949e24B80604cc670A3, lexchexAuth: 0xeAdeaD5C4A6747D4959489742c143bCDb95a01c2, From ed6d2c1e2f4de4e91183125f8e186200ecf386a8 Mon Sep 17 00:00:00 2001 From: detoo Date: Sun, 26 Apr 2026 17:15:36 -0700 Subject: [PATCH 02/20] wip: chore: prepare for production deployment --- script/deploy-parentco-factory.s.sol | 343 ++++++++++++++++----------- 1 file changed, 202 insertions(+), 141 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index 3699526..1c90f0d 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -5,7 +5,7 @@ import {Script} from "forge-std/Script.sol"; import {console2} from "forge-std/console2.sol"; import {ParentCoFactory} from "../src/ParentCoFactory.sol"; import {BorgAuth} from "../src/libs/auth.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {DeploymentConstants} from "./libs/DeploymentConstants.sol"; import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; import {CyberAgreementUtils} from "../test/libs/CyberAgreementUtils.sol"; @@ -19,30 +19,76 @@ contract DeployParentCoFactoryScript is Script { function run() public returns (ParentCoFactory parentCoFactory) { return runWithArgs( - vm.envUint("PRIVATE_KEY_MAIN"), - 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, // test corp payable - 0x42069BaBe92462393FaFdc653A88F958B64EC9A3 // test officer EOA + // TODO need production arguments +// { +// stable: 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, // Base USDC +// } + + // Base sepolia + { + chainId: BASE_SEPOLIA_CHAIN_ID, + deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), + saltStr: "ParentCoFactory.deploy.v2", + paymentToken: 0x036CbD53842c5426634e7929541eC2318f3dCF7e, // Base Sepolia USDC + segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), + segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", + segCoDocUri: "ipfs://parentco-test-segco-template", + boardConsentTemplateId: keccak256("ParentCo.Test2.BoardConsent.v1"), + boardConsentName: "ParentCo Test Board Consent", + boardConsentUri: "ipfs://parentco-test-board-consent-template", + parentCorpPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, + parentCorpOfficerAddress: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, + parentCoName: "Test ParentCo LLC", + parentCoType: "limited liability company", + parentCoJurisdiction: "Delaware", + parentCoContactDetails: "test@parentco.example", + parentCoDefaultDisputeResolution: "binding arbitration", + parentCoOfficerName: "Test ParentCo Officer", + parentCoOfficerContact: "test@parentco.example", + parentCoOfficerTitle: "Director", + parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a", + deployTestSubCorp: true + } ); } function runWithArgs( + uint256 chainId, uint256 deployerPrivateKey, - address corpPayable, - address officerAddress + string memory saltStr, + address paymentToken, + bytes32 segCoTemplateId, + string memory segCoDocName, + string memory segCoDocUri, + bytes32 boardConsentTemplateId, + string memory boardConsentName, + string memory boardConsentUri, + address parentCoPayable, + address parentCoOfficerAddress, + string memory parentCoName, + string memory parentCoType, + string memory parentCoJurisdiction, + string memory parentCoContactDetails, + string memory parentCoDefaultDisputeResolution, + string memory parentCoOfficerName, + string memory parentCoOfficerContact, + string memory parentCoOfficerTitle, + bytes memory parentEscrowSig, + bool deployTestSubCorp ) public returns (ParentCoFactory parentCoFactory) { - string memory parentCoOfficerName = "Test ParentCo Officer"; - string memory parentCoOfficerContact = "test@parentco.example"; - string memory parentCoOfficerTitle = "Director"; - DeploymentConstants.CoreDeployment memory deployment = DeploymentConstants - .coreV2(BASE_SEPOLIA_CHAIN_ID); + .coreV2(chainId); + + CyberAgreementRegistry registry = CyberAgreementRegistry( + deployment.cyberAgreementRegistry + ); address deployerAddress = vm.addr(deployerPrivateKey); - bytes32 salt = bytes32(keccak256("ParentCoFactory.deploy.v2")); + bytes32 salt = bytes32(keccak256(saltStr)); vm.startBroadcast(deployerPrivateKey); - BorgAuth auth = new BorgAuth{salt: salt}(deployerAddress); + BorgAuth parentCoFactoryAuth = new BorgAuth{salt: salt}(deployerAddress); parentCoFactory = ParentCoFactory( address( @@ -50,20 +96,22 @@ contract DeployParentCoFactoryScript is Script { address(new ParentCoFactory{salt: salt}()), abi.encodeWithSelector( ParentCoFactory.initialize.selector, - address(auth), + address(parentCoFactoryAuth), deployment.cyberAgreementRegistry, deployment.issuanceManagerFactory, deployment.cyberCorpSingleFactory, deployment.dealManagerFactory, deployment.roundManagerFactory, deployment.uriBuilder, - BASE_USDC + paymentToken ) ) ) ); - parentCoFactory.setParentCoOfficerEOA(officerAddress); + // Configure ParentCo officer and escrowed signature BEFORE revoking deployer ownership + + parentCoFactory.setParentCoOfficerEOA(parentCoOfficerAddress); parentCoFactory.setParentCoOfficerName(parentCoOfficerName); parentCoFactory.setParentCoOfficerContact(parentCoOfficerContact); parentCoFactory.setParentCoOfficerTitle(parentCoOfficerTitle); @@ -75,28 +123,40 @@ contract DeployParentCoFactoryScript is Script { address parentDealMgr, address parentRoundMgr ) = parentCoFactory.createParentCorp( - bytes32(keccak256("Test2 ParentCo LLC")), - "Test ParentCo LLC", - "limited liability company", - "Delaware", - "test@parentco.example", - "binding arbitration", - corpPayable + bytes32(keccak256(parentCoName)), // use parent corp name as the salt str + parentCoName, + parentCoType, + parentCoJurisdiction, + parentCoContactDetails, + parentCoDefaultDisputeResolution, + parentCoPayable ); - // Escrow signature bytes for parent signing path (placeholder/test value). - bytes memory parentEscrowSig = hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a"; parentCoFactory.setParentCoSignatureHash(parentEscrowSig); - CyberAgreementRegistry registry = CyberAgreementRegistry( + parentCoFactoryAuth.updateRole(parentCoOfficerAddress, parentCoFactoryAuth.OWNER_ROLE()); + parentCoFactoryAuth.updateRole(parentCoPayable, parentCoFactoryAuth.OWNER_ROLE()); + + console2.log("ParentCoFactory Auth:", address(parentCoFactoryAuth)); + console2.log( + "CyberAgreementRegistry:", deployment.cyberAgreementRegistry ); + console2.log("IssuanceManagerFactory:", deployment.issuanceManagerFactory); + console2.log("CyberCorpSingleFactory:", deployment.cyberCorpSingleFactory); + console2.log("DealManagerFactory:", deployment.dealManagerFactory); + console2.log("RoundManagerFactory:", deployment.roundManagerFactory); + console2.log("CertificateUriBuilder:", deployment.uriBuilder); + console2.log("ParentCoFactory (proxy):", address(parentCoFactory)); + console2.log("ParentCorp:", parentCorp); + console2.log("ParentAuth:", parentAuth); + console2.log("ParentIssuance:", parentIssuance); + console2.log("ParentDealMgr:", parentDealMgr); + console2.log("ParentRoundMgr:", parentRoundMgr); + console2.log(""); + // TODO create multisig txs instead // Create (or no-op if already present) templates used by deployCorpContractFor. - bytes32 segCoTemplateId = keccak256("ParentCo.Test2.SegCo.v1"); - bytes32 boardConsentTemplateId = keccak256( - "ParentCo.Test2.BoardConsent.v1" - ); string[] memory globalFields = new string[](8); globalFields[0] = "founderName"; @@ -115,134 +175,135 @@ contract DeployParentCoFactoryScript is Script { try registry.createTemplate( segCoTemplateId, - "ParentCo Test SegCo Agreement", - "ipfs://parentco-test-segco-template", + segCoDocName, + segCoDocUri, globalFields, partyFields ) - {} catch {} + { + console2.log("Founder Sig Pack template created:"); + console2.log(segCoTemplateId); + console2.log(" name: %s", segCoDocName); + console2.log(" URI: %s", segCoDocUri); + } catch { + console2.log("Founder Sig Pack template already exists, skipped"); + } try registry.createTemplate( boardConsentTemplateId, - "ParentCo Test Board Consent", - "ipfs://parentco-test-board-consent-template", + boardConsentName, + boardConsentUri, globalFields, partyFields ) - {} catch {} - - // Build SubCorp inputs matching ParentCoFactory's strict field checks. - uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); - string memory subCompanyName = "Test SubCo SPV 1"; - string memory subCompanyType = "series limited liability company"; - string memory subCompanyJurisdiction = "Delaware"; - string memory subCompanyContact = "subco@parentco.example"; - string memory subDisputeResolution = "binding arbitration"; - - string[] memory globalValues = new string[](8); - globalValues[0] = "Test Founder"; - globalValues[1] = "Test ParentCo Enterprise"; - globalValues[2] = subCompanyName; - globalValues[3] = subCompanyType; - globalValues[4] = subCompanyJurisdiction; - globalValues[5] = subCompanyContact; - globalValues[6] = "TSC1"; - globalValues[7] = "Test SubCo One"; - - string[] memory partyValues = new string[](2); - partyValues[0] = "Test Deployer Officer"; - partyValues[1] = "deployer@parentco.example"; - - CompanyOfficer memory subOfficer = CompanyOfficer({ - eoa: deployerAddress, - name: partyValues[0], - contact: partyValues[1], - title: "Founder" - }); - - // Pre-compute agreement id and signer signature expected by signContractFor. - address[] memory agreementParties = new address[](2); - agreementParties[0] = officerAddress; - agreementParties[1] = deployerAddress; - bytes32 agreementId = keccak256( - abi.encode(segCoTemplateId, subCorpSalt, globalValues, agreementParties) - ); + { + console2.log("Board Consent template created:"); + console2.log(boardConsentTemplateId); + console2.log(" name: %s", boardConsentName); + console2.log(" URI: %s", boardConsentUri); + } catch { + console2.log("Board Consent template already exists, skipped"); + } - ( - string memory legalContractUri, - , - string[] memory templateGlobalFields, - string[] memory templatePartyFields - ) = registry.getTemplateDetails(segCoTemplateId); - - bytes memory deployerSignature = CyberAgreementUtils.signAgreementTypedData( - vm, - registry.DOMAIN_SEPARATOR(), - registry.SIGNATUREDATA_TYPEHASH(), - agreementId, - legalContractUri, - templateGlobalFields, - templatePartyFields, - globalValues, - partyValues, - deployerPrivateKey - ); + console2.log(""); - ( - address subCorp, - address subAuth, - address subIssuance, - address subDealMgr, - address subRoundMgr, - address[] memory subCertPrinters, - bytes32 subAgreementId, - uint256[] memory subCertIds - ) = parentCoFactory.deployCorpContractFor( - subCorpSalt, - subCompanyName, - subCompanyType, - subCompanyJurisdiction, - subCompanyContact, - subDisputeResolution, - corpPayable, - subOfficer, - segCoTemplateId, - boardConsentTemplateId, + if (deployTestSubCorp) { + + // Build SubCorp inputs matching ParentCoFactory's strict field checks. + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + string memory subCompanyName = "Test SubCo SPV 1"; + string memory subCompanyType = "series limited liability company"; + string memory subCompanyJurisdiction = "Delaware"; + string memory subCompanyContact = "subco@parentco.example"; + string memory subDisputeResolution = "binding arbitration"; + + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = subCompanyName; + globalValues[3] = subCompanyType; + globalValues[4] = subCompanyJurisdiction; + globalValues[5] = subCompanyContact; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: deployerAddress, + name: partyValues[0], + contact: partyValues[1], + title: "Founder" + }); + + // Pre-compute agreement id and signer signature expected by signContractFor. + address[] memory agreementParties = new address[](2); + agreementParties[0] = parentCoOfficerAddress; + agreementParties[1] = deployerAddress; + bytes32 agreementId = keccak256( + abi.encode(segCoTemplateId, subCorpSalt, globalValues, agreementParties) + ); + + ( + string memory legalContractUri, + , + string[] memory templateGlobalFields, + string[] memory templatePartyFields + ) = registry.getTemplateDetails(segCoTemplateId); + + bytes memory deployerSignature = CyberAgreementUtils.signAgreementTypedData( + vm, + registry.DOMAIN_SEPARATOR(), + registry.SIGNATUREDATA_TYPEHASH(), + agreementId, + legalContractUri, + templateGlobalFields, + templatePartyFields, globalValues, partyValues, - deployerSignature, - deployerAddress + deployerPrivateKey ); - auth.updateRole(officerAddress, auth.OWNER_ROLE()); - auth.updateRole(corpPayable, auth.OWNER_ROLE()); + ( + address subCorp, + address subAuth, + address subIssuance, + address subDealMgr, + address subRoundMgr, + address[] memory subCertPrinters, + bytes32 subAgreementId, + uint256[] memory subCertIds + ) = parentCoFactory.deployCorpContractFor( + subCorpSalt, + subCompanyName, + subCompanyType, + subCompanyJurisdiction, + subCompanyContact, + subDisputeResolution, + parentCoPayable, + subOfficer, + segCoTemplateId, + boardConsentTemplateId, + globalValues, + partyValues, + deployerSignature, + deployerAddress + ); - console2.log("Auth:", address(auth)); - console2.log( - "CyberAgreementRegistry:", - deployment.cyberAgreementRegistry - ); - console2.log("IssuanceManagerFactory:", deployment.issuanceManagerFactory); - console2.log("CyberCorpSingleFactory:", deployment.cyberCorpSingleFactory); - console2.log("DealManagerFactory:", deployment.dealManagerFactory); - console2.log("RoundManagerFactory:", deployment.roundManagerFactory); - console2.log("CertificateUriBuilder:", deployment.uriBuilder); - console2.log("ParentCoFactory (proxy):", address(parentCoFactory)); - console2.log("ParentCorp:", parentCorp); - console2.log("ParentAuth:", parentAuth); - console2.log("ParentIssuance:", parentIssuance); - console2.log("ParentDealMgr:", parentDealMgr); - console2.log("ParentRoundMgr:", parentRoundMgr); - console2.log("SubCorp:", subCorp); - console2.log("SubAuth:", subAuth); - console2.log("SubIssuance:", subIssuance); - console2.log("SubDealMgr:", subDealMgr); - console2.log("SubRoundMgr:", subRoundMgr); - console2.log("SubAgreementId:"); - console2.logBytes32(subAgreementId); - console2.log("SubCertPrinters count:", subCertPrinters.length); - console2.log("SubCertIds count:", subCertIds.length); + console2.log("SubCorp:", subCorp); + console2.log("SubAuth:", subAuth); + console2.log("SubIssuance:", subIssuance); + console2.log("SubDealMgr:", subDealMgr); + console2.log("SubRoundMgr:", subRoundMgr); + console2.log("SubAgreementId:"); + console2.logBytes32(subAgreementId); + console2.log("SubCertPrinters count:", subCertPrinters.length); + console2.log("SubCertIds count:", subCertIds.length); + console2.log(""); + } vm.stopBroadcast(); } From 56d01e6b71abedf679f341aefe1942d8295cfca3 Mon Sep 17 00:00:00 2001 From: detoo Date: Sun, 26 Apr 2026 20:09:54 -0700 Subject: [PATCH 03/20] test: production deployment --- script/deploy-parentco-factory.s.sol | 160 +++++--------------------- test/DeployParentCoFactory.t.sol | 165 +++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 129 deletions(-) create mode 100644 test/DeployParentCoFactory.t.sol diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index 1c90f0d..a926c4a 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -7,6 +7,7 @@ import {ParentCoFactory} from "../src/ParentCoFactory.sol"; import {BorgAuth} from "../src/libs/auth.sol"; import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {DeploymentConstants} from "./libs/DeploymentConstants.sol"; +import {GnosisTransaction} from "./libs/safe.sol"; import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; import {CyberAgreementUtils} from "../test/libs/CyberAgreementUtils.sol"; import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; @@ -16,7 +17,7 @@ contract DeployParentCoFactoryScript is Script { address internal constant BASE_USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; - function run() public returns (ParentCoFactory parentCoFactory) { + function run() public returns (ParentCoFactory parentCoFactory, GnosisTransaction[] memory safeTxs) { return runWithArgs( // TODO need production arguments @@ -36,8 +37,8 @@ contract DeployParentCoFactoryScript is Script { boardConsentTemplateId: keccak256("ParentCo.Test2.BoardConsent.v1"), boardConsentName: "ParentCo Test Board Consent", boardConsentUri: "ipfs://parentco-test-board-consent-template", - parentCorpPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, - parentCorpOfficerAddress: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, + parentCoPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, + parentCoOfficerAddress: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, parentCoName: "Test ParentCo LLC", parentCoType: "limited liability company", parentCoJurisdiction: "Delaware", @@ -46,8 +47,7 @@ contract DeployParentCoFactoryScript is Script { parentCoOfficerName: "Test ParentCo Officer", parentCoOfficerContact: "test@parentco.example", parentCoOfficerTitle: "Director", - parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a", - deployTestSubCorp: true + parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" } ); } @@ -73,9 +73,8 @@ contract DeployParentCoFactoryScript is Script { string memory parentCoOfficerName, string memory parentCoOfficerContact, string memory parentCoOfficerTitle, - bytes memory parentEscrowSig, - bool deployTestSubCorp - ) public returns (ParentCoFactory parentCoFactory) { + bytes memory parentEscrowSig + ) public returns (ParentCoFactory parentCoFactory, GnosisTransaction[] memory safeTxs) { DeploymentConstants.CoreDeployment memory deployment = DeploymentConstants .coreV2(chainId); @@ -84,7 +83,7 @@ contract DeployParentCoFactoryScript is Script { ); address deployerAddress = vm.addr(deployerPrivateKey); - bytes32 salt = bytes32(keccak256(saltStr)); + bytes32 salt = bytes32(keccak256(bytes(saltStr))); vm.startBroadcast(deployerPrivateKey); @@ -123,7 +122,7 @@ contract DeployParentCoFactoryScript is Script { address parentDealMgr, address parentRoundMgr ) = parentCoFactory.createParentCorp( - bytes32(keccak256(parentCoName)), // use parent corp name as the salt str + bytes32(keccak256(bytes(parentCoName))), // use parent corp name as the salt str parentCoName, parentCoType, parentCoJurisdiction, @@ -155,7 +154,6 @@ contract DeployParentCoFactoryScript is Script { console2.log("ParentRoundMgr:", parentRoundMgr); console2.log(""); - // TODO create multisig txs instead // Create (or no-op if already present) templates used by deployCorpContractFor. string[] memory globalFields = new string[](8); @@ -172,136 +170,40 @@ contract DeployParentCoFactoryScript is Script { partyFields[0] = "name"; partyFields[1] = "contactDetails"; - try - registry.createTemplate( + safeTxs = new GnosisTransaction[](2); + // create Founder Sig Pack template + safeTxs[0] = GnosisTransaction({ + to: address(registry), + value: 0, + data: abi.encodeWithSelector( + registry.createTemplate.selector, segCoTemplateId, segCoDocName, segCoDocUri, globalFields, partyFields ) - { - console2.log("Founder Sig Pack template created:"); - console2.log(segCoTemplateId); - console2.log(" name: %s", segCoDocName); - console2.log(" URI: %s", segCoDocUri); - } catch { - console2.log("Founder Sig Pack template already exists, skipped"); - } - - try - registry.createTemplate( + }); + safeTxs[1] = GnosisTransaction({ + to: address(registry), + value: 0, + data: abi.encodeWithSelector( + registry.createTemplate.selector, boardConsentTemplateId, boardConsentName, boardConsentUri, globalFields, partyFields ) - { - console2.log("Board Consent template created:"); - console2.log(boardConsentTemplateId); - console2.log(" name: %s", boardConsentName); - console2.log(" URI: %s", boardConsentUri); - } catch { - console2.log("Board Consent template already exists, skipped"); - } - - console2.log(""); - - if (deployTestSubCorp) { - - // Build SubCorp inputs matching ParentCoFactory's strict field checks. - uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); - string memory subCompanyName = "Test SubCo SPV 1"; - string memory subCompanyType = "series limited liability company"; - string memory subCompanyJurisdiction = "Delaware"; - string memory subCompanyContact = "subco@parentco.example"; - string memory subDisputeResolution = "binding arbitration"; - - string[] memory globalValues = new string[](8); - globalValues[0] = "Test Founder"; - globalValues[1] = "Test ParentCo Enterprise"; - globalValues[2] = subCompanyName; - globalValues[3] = subCompanyType; - globalValues[4] = subCompanyJurisdiction; - globalValues[5] = subCompanyContact; - globalValues[6] = "TSC1"; - globalValues[7] = "Test SubCo One"; - - string[] memory partyValues = new string[](2); - partyValues[0] = "Test Deployer Officer"; - partyValues[1] = "deployer@parentco.example"; - - CompanyOfficer memory subOfficer = CompanyOfficer({ - eoa: deployerAddress, - name: partyValues[0], - contact: partyValues[1], - title: "Founder" - }); - - // Pre-compute agreement id and signer signature expected by signContractFor. - address[] memory agreementParties = new address[](2); - agreementParties[0] = parentCoOfficerAddress; - agreementParties[1] = deployerAddress; - bytes32 agreementId = keccak256( - abi.encode(segCoTemplateId, subCorpSalt, globalValues, agreementParties) - ); - - ( - string memory legalContractUri, - , - string[] memory templateGlobalFields, - string[] memory templatePartyFields - ) = registry.getTemplateDetails(segCoTemplateId); - - bytes memory deployerSignature = CyberAgreementUtils.signAgreementTypedData( - vm, - registry.DOMAIN_SEPARATOR(), - registry.SIGNATUREDATA_TYPEHASH(), - agreementId, - legalContractUri, - templateGlobalFields, - templatePartyFields, - globalValues, - partyValues, - deployerPrivateKey - ); - - ( - address subCorp, - address subAuth, - address subIssuance, - address subDealMgr, - address subRoundMgr, - address[] memory subCertPrinters, - bytes32 subAgreementId, - uint256[] memory subCertIds - ) = parentCoFactory.deployCorpContractFor( - subCorpSalt, - subCompanyName, - subCompanyType, - subCompanyJurisdiction, - subCompanyContact, - subDisputeResolution, - parentCoPayable, - subOfficer, - segCoTemplateId, - boardConsentTemplateId, - globalValues, - partyValues, - deployerSignature, - deployerAddress - ); - - console2.log("SubCorp:", subCorp); - console2.log("SubAuth:", subAuth); - console2.log("SubIssuance:", subIssuance); - console2.log("SubDealMgr:", subDealMgr); - console2.log("SubRoundMgr:", subRoundMgr); - console2.log("SubAgreementId:"); - console2.logBytes32(subAgreementId); - console2.log("SubCertPrinters count:", subCertPrinters.length); - console2.log("SubCertIds count:", subCertIds.length); + }); + + console2.log("Safe Txs for creating templates:"); + for (uint256 i = 0 ; i < safeTxs.length ; i++) { + console2.log(" #", i); + console2.log(" to:", safeTxs[i].to); + console2.log(" value:", safeTxs[i].value); + console2.log(" data:"); + console2.logBytes(safeTxs[i].data); console2.log(""); } diff --git a/test/DeployParentCoFactory.t.sol b/test/DeployParentCoFactory.t.sol new file mode 100644 index 0000000..de63c8c --- /dev/null +++ b/test/DeployParentCoFactory.t.sol @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {Test, Vm, console2} from "forge-std/Test.sol"; +import {CyberAgreementUtils} from "./libs/CyberAgreementUtils.sol"; +import {DeployParentCoFactoryScript} from "../script/deploy-parentco-factory.s.sol"; +import {GnosisTransaction} from "../script/libs/safe.sol"; +import {DeploymentConstants} from "../script/libs/DeploymentConstants.sol"; +import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; +import {ParentCoFactory} from "../src/ParentCoFactory.sol"; +import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; + +contract DeployParentCoFactoryTest is Test { + DeploymentConstants.CoreDeployment deployment; + + CyberAgreementRegistry registry; + + ParentCoFactory parentCoFactory; + address parentCoPayable; + + bytes32 segCoTemplateId = keccak256("ParentCo.SegCo.v1"); + bytes32 boardConsentTemplateId = keccak256("ParentCo.BoardConsent.v1"); + + address subCorpOfficer; + uint256 subCorpOfficerPrivKey; + + function setUp() public { + deployment = DeploymentConstants + .coreV2(block.chainid); + + registry = CyberAgreementRegistry(deployment.cyberAgreementRegistry); + + (parentCoPayable, ) = makeAddrAndKey("parentCoPayable"); + (subCorpOfficer, subCorpOfficerPrivKey) = makeAddrAndKey("subCorpOfficer"); + + GnosisTransaction[] memory safeTxs; + (parentCoFactory, safeTxs) = (new DeployParentCoFactoryScript()).runWithArgs({ + chainId: block.chainid, + deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), + saltStr: "ParentCoFactory.deploy.v1", + paymentToken: 0x036CbD53842c5426634e7929541eC2318f3dCF7e, // Base Sepolia USDC + segCoTemplateId: segCoTemplateId, + segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", + segCoDocUri: "ipfs://parentco-test-segco-template", + boardConsentTemplateId: boardConsentTemplateId, + boardConsentName: "ParentCo Board Consent", + boardConsentUri: "ipfs://parentco-test-board-consent-template", + parentCoPayable: parentCoPayable, + parentCoOfficerAddress: parentCoPayable, + parentCoName: "Test ParentCo LLC", + parentCoType: "limited liability company", + parentCoJurisdiction: "Delaware", + parentCoContactDetails: "test@parentco.example", + parentCoDefaultDisputeResolution: "binding arbitration", + parentCoOfficerName: "Test ParentCo Officer", + parentCoOfficerContact: "test@parentco.example", + parentCoOfficerTitle: "Director", + parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" // TODO simulate escrow sig + }); + + // simulate MetaLeX safe executing safe txs + for (uint256 i = 0; i < safeTxs.length; i++) { + vm.prank(deployment.metalexSafe); + (bool success,) = safeTxs[i].to.call{value: safeTxs[i].value}(safeTxs[i].data); + vm.assertTrue(success); + } + } + + // TODO WIP: verify results + function test_deploySubCorp() public { + // Build SubCorp inputs matching ParentCoFactory's strict field checks. + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + string memory subCompanyName = "Test SubCo SPV 1"; + string memory subCompanyType = "series limited liability company"; + string memory subCompanyJurisdiction = "Delaware"; + string memory subCompanyContact = "subco@parentco.example"; + string memory subDisputeResolution = "binding arbitration"; + + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = subCompanyName; + globalValues[3] = subCompanyType; + globalValues[4] = subCompanyJurisdiction; + globalValues[5] = subCompanyContact; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: partyValues[0], + contact: partyValues[1], + title: "Founder" + }); + + // Pre-compute agreement id and signer signature expected by signContractFor. + address[] memory agreementParties = new address[](2); + agreementParties[0] = parentCoPayable; + agreementParties[1] = subCorpOfficer; + bytes32 agreementId = keccak256( + abi.encode(segCoTemplateId, subCorpSalt, globalValues, agreementParties) + ); + + ( + string memory legalContractUri, + , + string[] memory templateGlobalFields, + string[] memory templatePartyFields + ) = registry.getTemplateDetails(segCoTemplateId); + + bytes memory subCorpOwnerSignature = CyberAgreementUtils.signAgreementTypedData( + vm, + registry.DOMAIN_SEPARATOR(), + registry.SIGNATUREDATA_TYPEHASH(), + agreementId, + legalContractUri, + templateGlobalFields, + templatePartyFields, + globalValues, + partyValues, + subCorpOfficerPrivKey + ); + + ( + address subCorp, + address subAuth, + address subIssuance, + address subDealMgr, + address subRoundMgr, + address[] memory subCertPrinters, + bytes32 subAgreementId, + uint256[] memory subCertIds + ) = parentCoFactory.deployCorpContractFor( + subCorpSalt, + subCompanyName, + subCompanyType, + subCompanyJurisdiction, + subCompanyContact, + subDisputeResolution, + parentCoPayable, + subOfficer, + segCoTemplateId, + boardConsentTemplateId, + globalValues, + partyValues, + subCorpOwnerSignature, + subCorpOfficer + ); + + console2.log("SubCorp:", subCorp); + console2.log("SubAuth:", subAuth); + console2.log("SubIssuance:", subIssuance); + console2.log("SubDealMgr:", subDealMgr); + console2.log("SubRoundMgr:", subRoundMgr); + console2.log("SubAgreementId:"); + console2.logBytes32(subAgreementId); + console2.log("SubCertPrinters count:", subCertPrinters.length); + console2.log("SubCertIds count:", subCertIds.length); + console2.log(""); + } +} From 4adca9d225c4d2a481fbc4513a3613d56ef68f15 Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 09:10:57 -0700 Subject: [PATCH 04/20] chore: multiple officers --- script/deploy-parentco-factory.s.sol | 127 +++++++++++++++++---------- script/libs/DeploymentConstants.sol | 31 +++++++ src/ParentCoFactory.sol | 45 ++++------ src/interfaces/ICyberCorp.sol | 1 + test/DeployParentCoFactory.t.sol | 90 +++++++++++++++---- 5 files changed, 208 insertions(+), 86 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index a926c4a..fa15336 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -18,45 +18,87 @@ contract DeployParentCoFactoryScript is Script { 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; function run() public returns (ParentCoFactory parentCoFactory, GnosisTransaction[] memory safeTxs) { - return - runWithArgs( - // TODO need production arguments -// { -// stable: 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913, // Base USDC -// } - - // Base sepolia - { - chainId: BASE_SEPOLIA_CHAIN_ID, - deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), - saltStr: "ParentCoFactory.deploy.v2", - paymentToken: 0x036CbD53842c5426634e7929541eC2318f3dCF7e, // Base Sepolia USDC - segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), - segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", - segCoDocUri: "ipfs://parentco-test-segco-template", - boardConsentTemplateId: keccak256("ParentCo.Test2.BoardConsent.v1"), - boardConsentName: "ParentCo Test Board Consent", - boardConsentUri: "ipfs://parentco-test-board-consent-template", - parentCoPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, - parentCoOfficerAddress: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, - parentCoName: "Test ParentCo LLC", - parentCoType: "limited liability company", - parentCoJurisdiction: "Delaware", - parentCoContactDetails: "test@parentco.example", - parentCoDefaultDisputeResolution: "binding arbitration", - parentCoOfficerName: "Test ParentCo Officer", - parentCoOfficerContact: "test@parentco.example", - parentCoOfficerTitle: "Director", - parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" - } - ); + // production +// CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](3); +// parentCoOfficers[0] = CompanyOfficer({ +// eoa: address(0), // TODO TBD +// name: "", // TODO TBC +// contact: "", // TODO TBD +// title: "Director" +// }); +// parentCoOfficers[1] = CompanyOfficer({ +// eoa: address(0), // TODO TBD +// name: "", // TODO TBC +// contact: "", // TODO TBD +// title: "Director" +// }); +// parentCoOfficers[2] = CompanyOfficer({ +// eoa: address(0), // TODO TBD +// name: "", // TODO TBC +// contact: "", // TODO TBD +// title: "Director" +// }); +// +// return runWithArgs({ +// chainId: BASE_USDC, +// deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), +// saltStr: "ParentCoFactory.deploy.v1", +// segCoTemplateId: keccak256("ParentCo.SegCo.v1"), +// segCoDocName: "UMIA FOUNDER/OPERATOR LEGAL PACK", +// segCoDocUri: "ipfs://bafybeicqgnzaa4zm7nlkrnuuf7xdyhughisnougerjliewynjtdaioiv5e", +// boardConsentTemplateId: keccak256("ParentCo.BoardConsent.v1"), +// boardConsentName: "ACTION BY UNANIMOUS WRITTEN CONSENT OF THE BOARD OF DIRECTORS OF UMIA LAUNCHER SPC", +// boardConsentUri: "ipfs://bafkreicr65qbif4vprnt5l2ovzqjbywwgmnply3sz7neb6qzjbb2njcrw4", +// parentCoPayable: 0x299FF70C049c0a6d591319fA7BaD86e24a671436, +// parentCoName: "UMIA LAUNCHER, SPC", +// parentCoType: "Segregated Portfolio Company", +// parentCoJurisdiction: "Cayman Islands", +// parentCoContactDetails: "c/o TTA Corporate Services Limited, Harbour Place, 2nd Floor, North Wing, 103 South Church Street, P.O. Box 472, George Town, Grand Cayman KY1-1106, Cayman Islands", +// parentCoDefaultDisputeResolution: "confidential, binding arbitration", +// parentCoOfficers: parentCoOfficers, +// parentEscrowSig: hex"" // TODO TBD +// }); + + // testnet + CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](2); + parentCoOfficers[0] = CompanyOfficer({ + eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, + name: "Test Officer 1", + contact: "test1@parentco.example", + title: "Director" + }); + parentCoOfficers[1] = CompanyOfficer({ + eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, + name: "Test Officer 2", + contact: "test2@parentco.example", + title: "Director" + }); + + return runWithArgs({ + chainId: BASE_SEPOLIA_CHAIN_ID, + deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), + saltStr: "ParentCoFactory.deploy.v2", + segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), + segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", + segCoDocUri: "ipfs://parentco-test-segco-template", + boardConsentTemplateId: keccak256("ParentCo.Test2.BoardConsent.v1"), + boardConsentName: "ParentCo Test Board Consent", + boardConsentUri: "ipfs://parentco-test-board-consent-template", + parentCoPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, + parentCoName: "Test ParentCo LLC", + parentCoType: "limited liability company", + parentCoJurisdiction: "Delaware", + parentCoContactDetails: "test@parentco.example", + parentCoDefaultDisputeResolution: "binding arbitration", + parentCoOfficers: parentCoOfficers, + parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" + }); } function runWithArgs( uint256 chainId, uint256 deployerPrivateKey, string memory saltStr, - address paymentToken, bytes32 segCoTemplateId, string memory segCoDocName, string memory segCoDocUri, @@ -64,19 +106,18 @@ contract DeployParentCoFactoryScript is Script { string memory boardConsentName, string memory boardConsentUri, address parentCoPayable, - address parentCoOfficerAddress, string memory parentCoName, string memory parentCoType, string memory parentCoJurisdiction, string memory parentCoContactDetails, string memory parentCoDefaultDisputeResolution, - string memory parentCoOfficerName, - string memory parentCoOfficerContact, - string memory parentCoOfficerTitle, + CompanyOfficer[] memory parentCoOfficers, bytes memory parentEscrowSig ) public returns (ParentCoFactory parentCoFactory, GnosisTransaction[] memory safeTxs) { - DeploymentConstants.CoreDeployment memory deployment = DeploymentConstants - .coreV2(chainId); + DeploymentConstants.Deps memory deps = DeploymentConstants.deps(chainId); + DeploymentConstants.CoreDeployment memory deployment = DeploymentConstants.coreV2(chainId); + + address paymentToken = deps.usdc; CyberAgreementRegistry registry = CyberAgreementRegistry( deployment.cyberAgreementRegistry @@ -110,10 +151,7 @@ contract DeployParentCoFactoryScript is Script { // Configure ParentCo officer and escrowed signature BEFORE revoking deployer ownership - parentCoFactory.setParentCoOfficerEOA(parentCoOfficerAddress); - parentCoFactory.setParentCoOfficerName(parentCoOfficerName); - parentCoFactory.setParentCoOfficerContact(parentCoOfficerContact); - parentCoFactory.setParentCoOfficerTitle(parentCoOfficerTitle); + parentCoFactory.setParentCoOfficers(parentCoOfficers); ( address parentCorp, @@ -133,7 +171,8 @@ contract DeployParentCoFactoryScript is Script { parentCoFactory.setParentCoSignatureHash(parentEscrowSig); - parentCoFactoryAuth.updateRole(parentCoOfficerAddress, parentCoFactoryAuth.OWNER_ROLE()); + for (uint256 i = 0; i < parentCoOfficers.length; i++) + parentCoFactoryAuth.updateRole(parentCoOfficers[i].eoa, parentCoFactoryAuth.OWNER_ROLE()); parentCoFactoryAuth.updateRole(parentCoPayable, parentCoFactoryAuth.OWNER_ROLE()); console2.log("ParentCoFactory Auth:", address(parentCoFactoryAuth)); diff --git a/script/libs/DeploymentConstants.sol b/script/libs/DeploymentConstants.sol index 52ebc7e..106f6fd 100644 --- a/script/libs/DeploymentConstants.sol +++ b/script/libs/DeploymentConstants.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.28; library DeploymentConstants { error UnsupportedChain(uint256 chainId); + uint256 internal constant ETH = 1; uint256 internal constant BASE = 8453; uint256 internal constant ETH_SEPOLIA = 11155111; @@ -25,6 +26,10 @@ library DeploymentConstants { address lexchexCondition; } + struct Deps { + address usdc; + } + /// @notice Latest CyberCorps V2 deployment constants. /// @dev Source: script/res/deployment-addresses.md function coreV2(uint256 chainId) @@ -69,4 +74,30 @@ library DeploymentConstants { }); } } + + function deps(uint256 chainId) + internal + pure + returns (Deps memory deps) + { + if (chainId == ETH) { + return Deps({ + usdc: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 + }); + } else if (chainId == BASE) { + return Deps({ + usdc: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 + }); + } else if (chainId == ETH_SEPOLIA) { + return Deps({ + usdc: 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 + }); + } else if (chainId == BASE_SEPOLIA) { + return Deps({ + usdc: 0x036CbD53842c5426634e7929541eC2318f3dCF7e + }); + } else { + revert UnsupportedChain(chainId); + } + } } diff --git a/src/ParentCoFactory.sol b/src/ParentCoFactory.sol index ec826e3..aa32b08 100644 --- a/src/ParentCoFactory.sol +++ b/src/ParentCoFactory.sol @@ -88,7 +88,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { bytes public parentCoSignatureHash; address public stable; // stored ParentCo officer details used in agreements - CompanyOfficer public parentCoOfficer; + CompanyOfficer[] public parentCoOfficers; // Parent corp (ParentCo) deployment record address public parentCorp; @@ -99,7 +99,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { bool public parentCorpCreated; //adjust storage gap based on new variable - uint256[38] private __gap; // keep storage gap similar to CyberCorpFactory + uint256[41] private __gap; // keep storage gap similar to CyberCorpFactory struct CyberCertData { string name; @@ -186,24 +186,10 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { stable = _stable; } - function setParentCoOfficer(CompanyOfficer memory _officer) public onlyOwner { - parentCoOfficer = _officer; - } - - function setParentCoOfficerEOA(address _eoa) public onlyOwner { - parentCoOfficer.eoa = _eoa; - } - - function setParentCoOfficerName(string memory _name) public onlyOwner { - parentCoOfficer.name = _name; - } - - function setParentCoOfficerContact(string memory _contact) public onlyOwner { - parentCoOfficer.contact = _contact; - } - - function setParentCoOfficerTitle(string memory _title) public onlyOwner { - parentCoOfficer.title = _title; + function setParentCoOfficers(CompanyOfficer[] memory _officers) public onlyOwner { + delete parentCoOfficers; + for (uint256 i = 0; i < _officers.length; i++) + parentCoOfficers.push(_officers[i]); } function setIssuanceManagerFactory(address _issuanceManagerFactory) external onlyOwner { @@ -342,7 +328,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { address roundMgr ) { if (parentCorpCreated) revert("ParentCorpAlreadyCreated"); - CompanyOfficer memory officer = parentCoOfficer; + CompanyOfficer memory officer = parentCoOfficers[0]; if (officer.eoa == address(0)) revert("ParentCoOfficerNotSet"); ( @@ -362,6 +348,11 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { officer ); + // Add the remaining officers + for (uint256 i = 1; i < parentCoOfficers.length; i++) { + ICyberCorp(corp).addOfficer(parentCoOfficers[i]); + } + parentCorp = corp; parentAuth = auth; parentIssuanceManager = issuance; @@ -380,7 +371,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { string memory companyContactDetails, string memory defaultDisputeResolution, address _companyPayable, - CompanyOfficer memory _officer, + CompanyOfficer memory _officer, // PC always has only one officer bytes32 _segCoTemplateId, bytes32 _boardConsentTempateId, string[] memory _globalValues, @@ -419,13 +410,13 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { // Effect: construct parties address[] memory partiesOverride = new address[](2); - partiesOverride[0] = parentCoOfficer.eoa; + partiesOverride[0] = parentCoOfficers[0].eoa; partiesOverride[1] = deployer; string[][] memory partyValuesOverride = new string[][](2); partyValuesOverride[0] = new string[](2); - partyValuesOverride[0][0] = parentCoOfficer.name; - partyValuesOverride[0][1] = parentCoOfficer.contact; + partyValuesOverride[0][0] = parentCoOfficers[0].name; + partyValuesOverride[0][1] = parentCoOfficers[0].contact; partyValuesOverride[1] = _partyValues; //create bytes32 salt @@ -463,7 +454,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { ICyberAgreementRegistry(registryAddress).signContractFor(deployer, agreementId, partyValuesOverride[1], signature, false, ""); ICyberAgreementRegistry(registryAddress).signContractWithEscrow( - parentCoOfficer.eoa, + parentCoOfficers[0].eoa, agreementId, partyValuesOverride[0], parentCoSignatureHash, @@ -488,7 +479,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { ); ICyberAgreementRegistry(registryAddress).signContractWithEscrow( - parentCoOfficer.eoa, + parentCoOfficers[0].eoa, meetingNotesId, meetingNotesPartyValues[0], parentCoSignatureHash, diff --git a/src/interfaces/ICyberCorp.sol b/src/interfaces/ICyberCorp.sol index 8f747a5..78fc523 100644 --- a/src/interfaces/ICyberCorp.sol +++ b/src/interfaces/ICyberCorp.sol @@ -68,6 +68,7 @@ interface ICyberCorp { function setDealManager(address _dealManager) external; function setRoundManager(address _roundManager) external; function roundManager() external view returns (address); + function addOfficer(CompanyOfficer memory _officer) external; function addEscrowedOfficerSignature(bytes calldata signature) external; function setEscrowedOfficerSignature(uint256 index, bytes calldata signature) external; function getEscrowedOfficerSignature(uint256 index) external view returns (bytes memory); diff --git a/test/DeployParentCoFactory.t.sol b/test/DeployParentCoFactory.t.sol index de63c8c..f67abba 100644 --- a/test/DeployParentCoFactory.t.sol +++ b/test/DeployParentCoFactory.t.sol @@ -8,64 +8,124 @@ import {GnosisTransaction} from "../script/libs/safe.sol"; import {DeploymentConstants} from "../script/libs/DeploymentConstants.sol"; import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; import {ParentCoFactory} from "../src/ParentCoFactory.sol"; +import {CyberCorp} from "../src/CyberCorp.sol"; import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; contract DeployParentCoFactoryTest is Test { - DeploymentConstants.CoreDeployment deployment; + DeploymentConstants.CoreDeployment coreDeployment; + DeploymentConstants.Deps deps; CyberAgreementRegistry registry; ParentCoFactory parentCoFactory; - address parentCoPayable; + address parentCoMultisig; + address parentCoOfficer1; + uint256 parentCoOfficer1PrivKey; + address parentCoOfficer2; + uint256 parentCoOfficer2PrivKey; bytes32 segCoTemplateId = keccak256("ParentCo.SegCo.v1"); bytes32 boardConsentTemplateId = keccak256("ParentCo.BoardConsent.v1"); + address subCorpPayable; address subCorpOfficer; uint256 subCorpOfficerPrivKey; function setUp() public { - deployment = DeploymentConstants - .coreV2(block.chainid); + coreDeployment = DeploymentConstants.coreV2(block.chainid); + deps = DeploymentConstants.deps(block.chainid); - registry = CyberAgreementRegistry(deployment.cyberAgreementRegistry); + registry = CyberAgreementRegistry(coreDeployment.cyberAgreementRegistry); - (parentCoPayable, ) = makeAddrAndKey("parentCoPayable"); + (parentCoMultisig, ) = makeAddrAndKey("parentCoMultisig"); + (parentCoOfficer1, parentCoOfficer1PrivKey) = makeAddrAndKey("parentCoOfficer1"); + (parentCoOfficer2, parentCoOfficer2PrivKey) = makeAddrAndKey("parentCoOfficer2"); + (subCorpPayable, ) = makeAddrAndKey("subCorpPayable"); (subCorpOfficer, subCorpOfficerPrivKey) = makeAddrAndKey("subCorpOfficer"); + CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](2); + parentCoOfficers[0] = CompanyOfficer({ + eoa: parentCoOfficer1, + name: "Test ParentCo Officer 1", + contact: "test1@parentco.example", + title: "Director" + }); + parentCoOfficers[1] = CompanyOfficer({ + eoa: parentCoOfficer2, + name: "Test ParentCo Officer 2", + contact: "test2@parentco.example", + title: "Director" + }); + GnosisTransaction[] memory safeTxs; (parentCoFactory, safeTxs) = (new DeployParentCoFactoryScript()).runWithArgs({ chainId: block.chainid, deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), saltStr: "ParentCoFactory.deploy.v1", - paymentToken: 0x036CbD53842c5426634e7929541eC2318f3dCF7e, // Base Sepolia USDC segCoTemplateId: segCoTemplateId, segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", segCoDocUri: "ipfs://parentco-test-segco-template", boardConsentTemplateId: boardConsentTemplateId, boardConsentName: "ParentCo Board Consent", boardConsentUri: "ipfs://parentco-test-board-consent-template", - parentCoPayable: parentCoPayable, - parentCoOfficerAddress: parentCoPayable, + parentCoPayable: parentCoMultisig, parentCoName: "Test ParentCo LLC", parentCoType: "limited liability company", parentCoJurisdiction: "Delaware", parentCoContactDetails: "test@parentco.example", parentCoDefaultDisputeResolution: "binding arbitration", - parentCoOfficerName: "Test ParentCo Officer", - parentCoOfficerContact: "test@parentco.example", - parentCoOfficerTitle: "Director", + parentCoOfficers: parentCoOfficers, parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" // TODO simulate escrow sig }); // simulate MetaLeX safe executing safe txs for (uint256 i = 0; i < safeTxs.length; i++) { - vm.prank(deployment.metalexSafe); + vm.prank(coreDeployment.metalexSafe); (bool success,) = safeTxs[i].to.call{value: safeTxs[i].value}(safeTxs[i].data); vm.assertTrue(success); } } + function test_parentCoOfficersInFactory() public { + (address eoa0, string memory name0, string memory contact0, string memory title0) = parentCoFactory.parentCoOfficers(0); + assertEq(eoa0, parentCoOfficer1); + assertEq(name0, "Test ParentCo Officer 1"); + assertEq(contact0, "test1@parentco.example"); + assertEq(title0, "Director"); + + (address eoa1, string memory name1, string memory contact1, string memory title1) = parentCoFactory.parentCoOfficers(1); + assertEq(eoa1, parentCoOfficer2); + assertEq(name1, "Test ParentCo Officer 2"); + assertEq(contact1, "test2@parentco.example"); + assertEq(title1, "Director"); + + vm.expectRevert(); + parentCoFactory.parentCoOfficers(2); + } + + function test_parentCorpOfficersInCyberCorp() public { + CyberCorp corp = CyberCorp(parentCoFactory.parentCorp()); + + (address eoa0, string memory name0, string memory contact0, string memory title0) = corp.companyOfficers(0); + assertEq(eoa0, parentCoOfficer1); + assertEq(name0, "Test ParentCo Officer 1"); + assertEq(contact0, "test1@parentco.example"); + assertEq(title0, "Director"); + + (address eoa1, string memory name1, string memory contact1, string memory title1) = corp.companyOfficers(1); + assertEq(eoa1, parentCoOfficer2); + assertEq(name1, "Test ParentCo Officer 2"); + assertEq(contact1, "test2@parentco.example"); + assertEq(title1, "Director"); + + assertTrue(corp.isCyberCORPOfficer(parentCoOfficer1)); + assertTrue(corp.isCyberCORPOfficer(parentCoOfficer2)); + assertFalse(corp.isCyberCORPOfficer(parentCoMultisig)); + + vm.expectRevert(); + corp.companyOfficers(2); + } + // TODO WIP: verify results function test_deploySubCorp() public { // Build SubCorp inputs matching ParentCoFactory's strict field checks. @@ -99,7 +159,7 @@ contract DeployParentCoFactoryTest is Test { // Pre-compute agreement id and signer signature expected by signContractFor. address[] memory agreementParties = new address[](2); - agreementParties[0] = parentCoPayable; + agreementParties[0] = parentCoOfficer1; agreementParties[1] = subCorpOfficer; bytes32 agreementId = keccak256( abi.encode(segCoTemplateId, subCorpSalt, globalValues, agreementParties) @@ -141,7 +201,7 @@ contract DeployParentCoFactoryTest is Test { subCompanyJurisdiction, subCompanyContact, subDisputeResolution, - parentCoPayable, + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, From d08aaa399cd9fd5d4cb04ec1d9f8b28bdb05b036 Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 11:47:19 -0700 Subject: [PATCH 05/20] feat: officer-specific escrowed signature --- script/deploy-parentco-factory.s.sol | 11 ++---- src/ParentCoFactory.sol | 53 ++++++++++++++++++++++++---- test/DeployParentCoFactory.t.sol | 45 +++++++++++++++++++++-- 3 files changed, 93 insertions(+), 16 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index fa15336..4dd5774 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -55,8 +55,7 @@ contract DeployParentCoFactoryScript is Script { // parentCoJurisdiction: "Cayman Islands", // parentCoContactDetails: "c/o TTA Corporate Services Limited, Harbour Place, 2nd Floor, North Wing, 103 South Church Street, P.O. Box 472, George Town, Grand Cayman KY1-1106, Cayman Islands", // parentCoDefaultDisputeResolution: "confidential, binding arbitration", -// parentCoOfficers: parentCoOfficers, -// parentEscrowSig: hex"" // TODO TBD +// parentCoOfficers: parentCoOfficers // }); // testnet @@ -90,8 +89,7 @@ contract DeployParentCoFactoryScript is Script { parentCoJurisdiction: "Delaware", parentCoContactDetails: "test@parentco.example", parentCoDefaultDisputeResolution: "binding arbitration", - parentCoOfficers: parentCoOfficers, - parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" + parentCoOfficers: parentCoOfficers }); } @@ -111,8 +109,7 @@ contract DeployParentCoFactoryScript is Script { string memory parentCoJurisdiction, string memory parentCoContactDetails, string memory parentCoDefaultDisputeResolution, - CompanyOfficer[] memory parentCoOfficers, - bytes memory parentEscrowSig + CompanyOfficer[] memory parentCoOfficers ) public returns (ParentCoFactory parentCoFactory, GnosisTransaction[] memory safeTxs) { DeploymentConstants.Deps memory deps = DeploymentConstants.deps(chainId); DeploymentConstants.CoreDeployment memory deployment = DeploymentConstants.coreV2(chainId); @@ -169,8 +166,6 @@ contract DeployParentCoFactoryScript is Script { parentCoPayable ); - parentCoFactory.setParentCoSignatureHash(parentEscrowSig); - for (uint256 i = 0; i < parentCoOfficers.length; i++) parentCoFactoryAuth.updateRole(parentCoOfficers[i].eoa, parentCoFactoryAuth.OWNER_ROLE()); parentCoFactoryAuth.updateRole(parentCoPayable, parentCoFactoryAuth.OWNER_ROLE()); diff --git a/src/ParentCoFactory.sol b/src/ParentCoFactory.sol index aa32b08..19007f5 100644 --- a/src/ParentCoFactory.sol +++ b/src/ParentCoFactory.sol @@ -55,6 +55,7 @@ import "./storage/CyberCertPrinterStorage.sol"; import "./libs/auth.sol"; import "@openzeppelin/contracts/utils/Create2.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; @@ -74,6 +75,7 @@ interface ICyberCorpLocal { contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { using Strings for string; + using ECDSA for bytes32; error InvalidSalt(); error RoundManagerAlreadyExists(); @@ -90,6 +92,10 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { // stored ParentCo officer details used in agreements CompanyOfficer[] public parentCoOfficers; + // EIP-712 + bytes32 public DOMAIN_SEPARATOR; + bytes32 public ESCROW_AUTHORIZATION_TYPEHASH; + // Parent corp (ParentCo) deployment record address public parentCorp; address public parentAuth; @@ -99,7 +105,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { bool public parentCorpCreated; //adjust storage gap based on new variable - uint256[41] private __gap; // keep storage gap similar to CyberCorpFactory + uint256[39] private __gap; // keep storage gap similar to CyberCorpFactory struct CyberCertData { string name; @@ -155,6 +161,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { error GlobalOrPartyValuesMismatch(); error OfficerValuesMismatch(); + error UnauthorizedEscrowSigner(); function initialize( address _auth, @@ -176,6 +183,17 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { roundManagerFactory = _roundManagerFactory; uriBuilder = _uriBuilder; stable = _stable; + + DOMAIN_SEPARATOR = keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256("ParentCoFactory"), + keccak256("1"), + block.chainid, + address(this) + ) + ); + ESCROW_AUTHORIZATION_TYPEHASH = keccak256("EscrowAuthorization(address factory)"); } function setParentCoSignatureHash(bytes memory _parentCoSignatureHash) public onlyOwner { @@ -192,6 +210,16 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { parentCoOfficers.push(_officers[i]); } + function escrowAuthorizationHash() public view returns (bytes32) { + return keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + keccak256(abi.encode(ESCROW_AUTHORIZATION_TYPEHASH, address(this))) + ) + ); + } + function setIssuanceManagerFactory(address _issuanceManagerFactory) external onlyOwner { address old = issuanceManagerFactory; issuanceManagerFactory = _issuanceManagerFactory; @@ -391,6 +419,19 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { uint256[] memory certIds ) { + // Resolve which officer pre-signed the escrow authorization + address escrowSigner = escrowAuthorizationHash().recover(parentCoSignatureHash); + CompanyOfficer memory signingOfficer; + bool signerFound; + for (uint256 i = 0; i < parentCoOfficers.length; i++) { + if (parentCoOfficers[i].eoa == escrowSigner) { + signingOfficer = parentCoOfficers[i]; + signerFound = true; + break; + } + } + if (!signerFound) revert UnauthorizedEscrowSigner(); + // Check: validate key fields if (_partyValues.length < 2 @@ -410,13 +451,13 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { // Effect: construct parties address[] memory partiesOverride = new address[](2); - partiesOverride[0] = parentCoOfficers[0].eoa; + partiesOverride[0] = signingOfficer.eoa; partiesOverride[1] = deployer; string[][] memory partyValuesOverride = new string[][](2); partyValuesOverride[0] = new string[](2); - partyValuesOverride[0][0] = parentCoOfficers[0].name; - partyValuesOverride[0][1] = parentCoOfficers[0].contact; + partyValuesOverride[0][0] = signingOfficer.name; + partyValuesOverride[0][1] = signingOfficer.contact; partyValuesOverride[1] = _partyValues; //create bytes32 salt @@ -454,7 +495,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { ICyberAgreementRegistry(registryAddress).signContractFor(deployer, agreementId, partyValuesOverride[1], signature, false, ""); ICyberAgreementRegistry(registryAddress).signContractWithEscrow( - parentCoOfficers[0].eoa, + signingOfficer.eoa, agreementId, partyValuesOverride[0], parentCoSignatureHash, @@ -479,7 +520,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { ); ICyberAgreementRegistry(registryAddress).signContractWithEscrow( - parentCoOfficers[0].eoa, + signingOfficer.eoa, meetingNotesId, meetingNotesPartyValues[0], parentCoSignatureHash, diff --git a/test/DeployParentCoFactory.t.sol b/test/DeployParentCoFactory.t.sol index f67abba..84a87f1 100644 --- a/test/DeployParentCoFactory.t.sol +++ b/test/DeployParentCoFactory.t.sol @@ -74,8 +74,7 @@ contract DeployParentCoFactoryTest is Test { parentCoJurisdiction: "Delaware", parentCoContactDetails: "test@parentco.example", parentCoDefaultDisputeResolution: "binding arbitration", - parentCoOfficers: parentCoOfficers, - parentEscrowSig: hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" // TODO simulate escrow sig + parentCoOfficers: parentCoOfficers }); // simulate MetaLeX safe executing safe txs @@ -84,6 +83,12 @@ contract DeployParentCoFactoryTest is Test { (bool success,) = safeTxs[i].to.call{value: safeTxs[i].value}(safeTxs[i].data); vm.assertTrue(success); } + + // Set escrow sig: parentCoOfficer1 signs the factory-specific EIP-712 authorization + bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(parentCoOfficer1PrivKey, escrowDigest); + vm.prank(parentCoOfficer1); + parentCoFactory.setParentCoSignatureHash(abi.encodePacked(r, s, v)); } function test_parentCoOfficersInFactory() public { @@ -126,6 +131,42 @@ contract DeployParentCoFactoryTest is Test { corp.companyOfficers(2); } + function test_deploySubCorp_revertIfEscrowSignerNotOfficer() public { + (, uint256 nonOfficerKey) = makeAddrAndKey("nonOfficer"); + bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(nonOfficerKey, escrowDigest); + vm.prank(parentCoOfficer1); + parentCoFactory.setParentCoSignatureHash(abi.encodePacked(r, s, v)); + + CompanyOfficer memory dummyOfficer = CompanyOfficer({eoa: address(0), name: "", contact: "", title: ""}); + string[] memory empty = new string[](0); + vm.expectRevert(ParentCoFactory.UnauthorizedEscrowSigner.selector); + parentCoFactory.deployCorpContractFor( + 0, "", "", "", "", "", address(0), dummyOfficer, + bytes32(0), bytes32(0), empty, empty, hex"", address(0) + ); + } + + function test_deploySubCorp_revertIfEscrowSigForWrongFactory() public { + // Officer signs a valid EIP-712 sig but with address(0) as the factory field + bytes32 wrongDigest = keccak256(abi.encodePacked( + "\x19\x01", + parentCoFactory.DOMAIN_SEPARATOR(), + keccak256(abi.encode(parentCoFactory.ESCROW_AUTHORIZATION_TYPEHASH(), address(0))) + )); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(parentCoOfficer1PrivKey, wrongDigest); + vm.prank(parentCoOfficer1); + parentCoFactory.setParentCoSignatureHash(abi.encodePacked(r, s, v)); + + CompanyOfficer memory dummyOfficer = CompanyOfficer({eoa: address(0), name: "", contact: "", title: ""}); + string[] memory empty = new string[](0); + vm.expectRevert(ParentCoFactory.UnauthorizedEscrowSigner.selector); + parentCoFactory.deployCorpContractFor( + 0, "", "", "", "", "", address(0), dummyOfficer, + bytes32(0), bytes32(0), empty, empty, hex"", address(0) + ); + } + // TODO WIP: verify results function test_deploySubCorp() public { // Build SubCorp inputs matching ParentCoFactory's strict field checks. From e5a1d4d0f744ff698dc4389288e46f1fc41e6872 Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 13:32:14 -0700 Subject: [PATCH 06/20] chore: prepare for production deployment with Safe txs json output --- script/deploy-parentco-factory.s.sol | 163 ++++++++++++++------------- script/libs/SafeUtils.sol | 71 ++++++++++++ 2 files changed, 155 insertions(+), 79 deletions(-) create mode 100644 script/libs/SafeUtils.sol diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index 4dd5774..adfe13a 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -1,96 +1,93 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; -import {Script} from "forge-std/Script.sol"; -import {console2} from "forge-std/console2.sol"; -import {ParentCoFactory} from "../src/ParentCoFactory.sol"; +import "./libs/SafeUtils.sol"; import {BorgAuth} from "../src/libs/auth.sol"; -import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {DeploymentConstants} from "./libs/DeploymentConstants.sol"; -import {GnosisTransaction} from "./libs/safe.sol"; +import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; import {CyberAgreementUtils} from "../test/libs/CyberAgreementUtils.sol"; -import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; +import {DeploymentConstants} from "./libs/DeploymentConstants.sol"; +import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {GnosisTransaction} from "./libs/safe.sol"; +import {ParentCoFactory} from "../src/ParentCoFactory.sol"; +import {Script} from "forge-std/Script.sol"; +import {console2} from "forge-std/console2.sol"; contract DeployParentCoFactoryScript is Script { uint256 internal constant BASE_SEPOLIA_CHAIN_ID = 84532; - address internal constant BASE_USDC = - 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; + uint256 internal constant BASE_CHAIN_ID = 8453; function run() public returns (ParentCoFactory parentCoFactory, GnosisTransaction[] memory safeTxs) { + // production -// CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](3); + + address[] memory eoas = vm.envAddress("PARENT_CO_OFFICER_EOAS", ","); + string[] memory names = vm.envString("PARENT_CO_OFFICER_NAMES", ","); + string[] memory contacts = vm.envString("PARENT_CO_OFFICER_CONTACTS", ","); + string[] memory titles = vm.envString("PARENT_CO_OFFICER_TITLES", ","); + CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](eoas.length); + for (uint256 i = 0; i < eoas.length ; i++) { + parentCoOfficers[i] = CompanyOfficer({ + eoa: eoas[i], + name: names[i], + contact: contacts[i], + title: titles[i] + }); + } + + return runWithArgs({ + chainId: DeploymentConstants.BASE, + deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), + saltStr: "ParentCoFactory.deploy.v1", + segCoTemplateId: keccak256("ParentCo.SegCo.v1"), + segCoDocName: "UMIA FOUNDER/OPERATOR LEGAL PACK", + segCoDocUri: "ipfs://bafybeicqgnzaa4zm7nlkrnuuf7xdyhughisnougerjliewynjtdaioiv5e", + boardConsentTemplateId: keccak256("ParentCo.BoardConsent.v1"), + boardConsentName: "ACTION BY UNANIMOUS WRITTEN CONSENT OF THE BOARD OF DIRECTORS OF UMIA LAUNCHER SPC", + boardConsentUri: "ipfs://bafkreicr65qbif4vprnt5l2ovzqjbywwgmnply3sz7neb6qzjbb2njcrw4", + parentCoPayable: 0x299FF70C049c0a6d591319fA7BaD86e24a671436, + parentCoName: "UMIA LAUNCHER, SPC", + parentCoType: "Segregated Portfolio Company", + parentCoJurisdiction: "Cayman Islands", + parentCoContactDetails: "c/o TTA Corporate Services Limited, Harbour Place, 2nd Floor, North Wing, 103 South Church Street, P.O. Box 472, George Town, Grand Cayman KY1-1106, Cayman Islands", + parentCoDefaultDisputeResolution: "confidential, binding arbitration", + parentCoOfficers: parentCoOfficers + }); + +// // testnet +// +// CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](2); // parentCoOfficers[0] = CompanyOfficer({ -// eoa: address(0), // TODO TBD -// name: "", // TODO TBC -// contact: "", // TODO TBD +// eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, +// name: "Test Officer 1", +// contact: "test1@parentco.example", // title: "Director" // }); // parentCoOfficers[1] = CompanyOfficer({ -// eoa: address(0), // TODO TBD -// name: "", // TODO TBC -// contact: "", // TODO TBD -// title: "Director" -// }); -// parentCoOfficers[2] = CompanyOfficer({ -// eoa: address(0), // TODO TBD -// name: "", // TODO TBC -// contact: "", // TODO TBD +// eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, +// name: "Test Officer 2", +// contact: "test2@parentco.example", // title: "Director" // }); // // return runWithArgs({ -// chainId: BASE_USDC, +// chainId: DeploymentConstants.BASE_SEPOLIA, // deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), -// saltStr: "ParentCoFactory.deploy.v1", -// segCoTemplateId: keccak256("ParentCo.SegCo.v1"), -// segCoDocName: "UMIA FOUNDER/OPERATOR LEGAL PACK", -// segCoDocUri: "ipfs://bafybeicqgnzaa4zm7nlkrnuuf7xdyhughisnougerjliewynjtdaioiv5e", -// boardConsentTemplateId: keccak256("ParentCo.BoardConsent.v1"), -// boardConsentName: "ACTION BY UNANIMOUS WRITTEN CONSENT OF THE BOARD OF DIRECTORS OF UMIA LAUNCHER SPC", -// boardConsentUri: "ipfs://bafkreicr65qbif4vprnt5l2ovzqjbywwgmnply3sz7neb6qzjbb2njcrw4", -// parentCoPayable: 0x299FF70C049c0a6d591319fA7BaD86e24a671436, -// parentCoName: "UMIA LAUNCHER, SPC", -// parentCoType: "Segregated Portfolio Company", -// parentCoJurisdiction: "Cayman Islands", -// parentCoContactDetails: "c/o TTA Corporate Services Limited, Harbour Place, 2nd Floor, North Wing, 103 South Church Street, P.O. Box 472, George Town, Grand Cayman KY1-1106, Cayman Islands", -// parentCoDefaultDisputeResolution: "confidential, binding arbitration", +// saltStr: "ParentCoFactory.deploy.v2", +// segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), +// segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", +// segCoDocUri: "ipfs://parentco-test-segco-template", +// boardConsentTemplateId: keccak256("ParentCo.Test2.BoardConsent.v1"), +// boardConsentName: "ParentCo Test Board Consent", +// boardConsentUri: "ipfs://parentco-test-board-consent-template", +// parentCoPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, +// parentCoName: "Test ParentCo LLC", +// parentCoType: "limited liability company", +// parentCoJurisdiction: "Delaware", +// parentCoContactDetails: "test@parentco.example", +// parentCoDefaultDisputeResolution: "binding arbitration", // parentCoOfficers: parentCoOfficers // }); - - // testnet - CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](2); - parentCoOfficers[0] = CompanyOfficer({ - eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, - name: "Test Officer 1", - contact: "test1@parentco.example", - title: "Director" - }); - parentCoOfficers[1] = CompanyOfficer({ - eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, - name: "Test Officer 2", - contact: "test2@parentco.example", - title: "Director" - }); - - return runWithArgs({ - chainId: BASE_SEPOLIA_CHAIN_ID, - deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), - saltStr: "ParentCoFactory.deploy.v2", - segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), - segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", - segCoDocUri: "ipfs://parentco-test-segco-template", - boardConsentTemplateId: keccak256("ParentCo.Test2.BoardConsent.v1"), - boardConsentName: "ParentCo Test Board Consent", - boardConsentUri: "ipfs://parentco-test-board-consent-template", - parentCoPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, - parentCoName: "Test ParentCo LLC", - parentCoType: "limited liability company", - parentCoJurisdiction: "Delaware", - parentCoContactDetails: "test@parentco.example", - parentCoDefaultDisputeResolution: "binding arbitration", - parentCoOfficers: parentCoOfficers - }); } function runWithArgs( @@ -123,6 +120,16 @@ contract DeployParentCoFactoryScript is Script { address deployerAddress = vm.addr(deployerPrivateKey); bytes32 salt = bytes32(keccak256(bytes(saltStr))); + console2.log("==== Configs ===="); + for (uint256 i = 0; i < parentCoOfficers.length; i++) { + console2.log("parentCoOfficers %d:", i); + console2.log(" EOA: %s", parentCoOfficers[i].eoa); + console2.log(" name: %s", parentCoOfficers[i].name); + console2.log(" contact: %s", parentCoOfficers[i].contact); + console2.log(" title: %s", parentCoOfficers[i].title); + console2.log(""); + } + vm.startBroadcast(deployerPrivateKey); BorgAuth parentCoFactoryAuth = new BorgAuth{salt: salt}(deployerAddress); @@ -170,6 +177,7 @@ contract DeployParentCoFactoryScript is Script { parentCoFactoryAuth.updateRole(parentCoOfficers[i].eoa, parentCoFactoryAuth.OWNER_ROLE()); parentCoFactoryAuth.updateRole(parentCoPayable, parentCoFactoryAuth.OWNER_ROLE()); + console2.log("==== Deployed ===="); console2.log("ParentCoFactory Auth:", address(parentCoFactoryAuth)); console2.log( "CyberAgreementRegistry:", @@ -231,15 +239,12 @@ contract DeployParentCoFactoryScript is Script { ) }); - console2.log("Safe Txs for creating templates:"); - for (uint256 i = 0 ; i < safeTxs.length ; i++) { - console2.log(" #", i); - console2.log(" to:", safeTxs[i].to); - console2.log(" value:", safeTxs[i].value); - console2.log(" data:"); - console2.logBytes(safeTxs[i].data); - console2.log(""); - } + string memory safeTxJson = SafeUtils.formatSafeTxJson(safeTxs); + + console2.log("Safe tx JSON (can be imported to Safe Transaction Builder):"); + console2.log("==== JSON data start ===="); + console2.log(safeTxJson); + console2.log("==== JSON data end ===="); vm.stopBroadcast(); } diff --git a/script/libs/SafeUtils.sol b/script/libs/SafeUtils.sol new file mode 100644 index 0000000..def476b --- /dev/null +++ b/script/libs/SafeUtils.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.20; + +import {Vm, console2} from "forge-std/Test.sol"; +import {GnosisTransaction} from "./safe.sol"; + +// Access hidden cheatcodes +interface EnhancedVm is Vm { + function serializeJsonType(string calldata typeDescription, bytes memory value) external pure returns (string memory json); +} + +library SafeUtils { + EnhancedVm constant vm = EnhancedVm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + struct SafeTxImport { + string version; + string chainId; + uint256 createdAt; + SafeTxMeta meta; + SafeTx[] transactions; + } + + struct SafeTxMeta { + string name; + string description; + string txBuilderVersion; + string createdFromSafeAddress; + string createdFromOwnerAddress; + string checksum; + } + + struct SafeTx { + address to; + string value; + bytes data; + } + + function formatSafeTxJson(GnosisTransaction[] memory safeTxs) internal returns (string memory) { + SafeTx[] memory convertedSafeTxs = new SafeTx[](safeTxs.length); + for (uint256 i = 0; i < safeTxs.length; i++) { + convertedSafeTxs[i] = SafeTx({ + to: safeTxs[i].to, + value: vm.toString(safeTxs[i].value), + data: safeTxs[i].data + }); + } + + return vm.serializeJsonType( + // it is important to include the input argument names as the utility will use them + "SafeTxImport(string version,string chainId,uint256 createdAt,SafeTxMeta meta,SafeTx[] transactions)SafeTxMeta(string name,string description,string txBuilderVersion,string createdFromSafeAddress,string createdFromOwnerAddress,string checksum)SafeTx(address to,string value,bytes data)", + abi.encode(SafeTxImport({ + version: "1.0", + chainId: "1", + createdAt: block.timestamp * 1000, + meta: SafeTxMeta({ + name: "Transactions Batch", + description: "", + txBuilderVersion: "", + createdFromSafeAddress: "", + createdFromOwnerAddress: "", + checksum: "" + }), + transactions: convertedSafeTxs + })) + ); + } + + function parseSafeTxJson(string memory json) internal returns (SafeTxImport memory) { + return abi.decode(vm.parseJson(json), (SafeTxImport)); + } +} From d1eec2234b66fa3b438f7d55d574005e1e30682c Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 14:05:36 -0700 Subject: [PATCH 07/20] test: add ownership tests. Remove unused scripts --- script/deploy-umia.factory.sol | 208 ------------------------------- test/DeployParentCoFactory.t.sol | 24 ++++ 2 files changed, 24 insertions(+), 208 deletions(-) delete mode 100644 script/deploy-umia.factory.sol diff --git a/script/deploy-umia.factory.sol b/script/deploy-umia.factory.sol deleted file mode 100644 index 2b24328..0000000 --- a/script/deploy-umia.factory.sol +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; - -import {Script} from "forge-std/Script.sol"; -import {console} from "forge-std/console.sol"; - -import {CyberAgreementUtils} from "../test/libs/CyberAgreementUtils.sol"; -import {MetaDAOFactory} from "../src/MetaDAOFactory.sol"; -import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; -import {IssuanceManagerFactory} from "../src/IssuanceManagerFactory.sol"; -import {IssuanceManager} from "../src/IssuanceManager.sol"; -import {CyberCorpSingleFactory} from "../src/CyberCorpSingleFactory.sol"; -import {CyberCorp} from "../src/CyberCorp.sol"; -import {DealManagerFactory, DealManager} from "../src/DealManagerFactory.sol"; -import {RoundManagerFactory, RoundManager} from "../src/RoundManagerFactory.sol"; -import {CertificateUriBuilder} from "../src/CertificateUriBuilder.sol"; -import {CyberCertPrinter} from "../src/CyberCertPrinter.sol"; -import {CyberScrip} from "../src/CyberScrip.sol"; -import {BorgAuth} from "../src/libs/auth.sol"; -import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; - -contract DeployScript is Script { - // Hard-coded since we don't have programmatic access to CyberAgreementRegistry's underlying types - string constant DOMAIN_SEPARATOR_TYPE = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"; - string constant ESCROW_SIGNATUREDATA_TYPE = "EscrowSignatureData(string legalContractUri,string[] partyFields,string[] partyValues)"; - - struct DomainSeparator { - string name; - string version; - uint256 chainId; - address verifyingContract; - } - - struct EscrowSignatureData { - string legalContractUri; - string[] partyFields; - string[] partyValues; - } - - function run() public returns ( - CyberAgreementRegistry registry, - MetaDAOFactory metaDAOFactory - ) { - return runWithArgs( - vm.envUint("PRIVATE_KEY_MAIN"), // deployerPrivateKey - 0x59026c9A3871505c8E5fb0B021e274a0B28547F6, // corpPayable - 0x76A6168B69f8f1b27E06dC77a30F2D1C92733e7A, // officerAddress - hex"73f62ac9b08c813401a02a16a920a106e525ac65dff992dccfd2cb42e5423db6725bb1b4d6e0244a635665f4965514512253613e3b032491f7ec85c2f657154e1a" // metadaoEscrowSig - ); - } - - function runWithArgs( - uint256 deployerPrivateKey, - address corpPayable, - address officerAddress, - bytes memory metadaoEscrowSig - ) public returns (CyberAgreementRegistry registry, MetaDAOFactory metaDAOFactory) { - // Other configs - string memory metaDAOOfficerName = "Test Umia Officer"; - string memory metaDAOOfficerContact = "Test Contact"; - string memory metaDAOOfficerTitle = "Director & Management Shareholder"; - - address deployerAddress = vm.addr(deployerPrivateKey); - vm.startBroadcast(deployerPrivateKey); - - bytes32 salt = bytes32(keccak256("UmiaFactory.deploy.v1")); - - address stable = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; // USDC @ Base - - BorgAuth auth = new BorgAuth{salt: salt}(deployerAddress); - - address registry = 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134; - // Create templates - - string[] memory globalFields = new string[](8); - globalFields[0] = "founderName"; - globalFields[1] = "enterpriseName"; - globalFields[2] = "companyName"; - globalFields[3] = "companyType"; - globalFields[4] = "companyJurisdiction"; - globalFields[5] = "companyContactDetails"; - globalFields[6] = "tokenSymbol"; - globalFields[7] = "tokenName"; - string[] memory partyFields = new string[](2); - partyFields[0] = "name"; - partyFields[1] = "contactDetails"; - - // Create template for SegCo - string memory segCoAgreementTitle = "Test Umia Futarchy Governance SPC - SegCo combined v 1.0"; - string memory segCoAgreementUri = "ipfs://bafybeifpvfwxfmobk7nhflsczqiynp3ca5urvyk3duh7s3rwptcnfzhuje"; - - // Create template for Board Consent - string memory boardConsentTitle = "Test Futarchy Governance SPC - Board Consent - Approval of SegCo v 1.0"; - string memory boardConsentUri = "ipfs://bafkreic7dscoigvwjc23vzvkmzophm34kpafu6nrctykq5bif63lqvpuoa"; - - - address uriBuilder = 0x5500c095ea7dE6F8a5E15949e24B80604cc670A3; - - address issuanceManagerFactory = 0xA32547aAdAA4975082D729c79e79dBaE4385EBCf; - - address cyberCorpSingleFactory = 0xc8e084D3f8B3b326FCc894C7afD28F4904196406; - - address dealManagerFactory = 0x975df8A99C895d04ae158F8C91Ba562Fce3ECDA3; - - // upgrade CyberAgreementRegistry - address newAgreementRegistryImplementation = address(new CyberAgreementRegistry{salt: salt}()); - CyberAgreementRegistry(registry).upgradeToAndCall(newAgreementRegistryImplementation, ""); - - MetaDAOFactory metaDAOFactory = MetaDAOFactory( - address( - new ERC1967Proxy{salt: salt}( - address(new MetaDAOFactory{salt: salt}()), - abi.encodeWithSelector( - MetaDAOFactory.initialize.selector, - address(auth), - address(registry), - address(issuanceManagerFactory), - address(cyberCorpSingleFactory), - address(dealManagerFactory), - address(0), - address(uriBuilder), - address(stable) - ) - ) - ) - ); - - // Configure MetaDAO officer and escrowed signature BEFORE revoking deployer ownership - metaDAOFactory.setMetaDAOOfficerEOA(officerAddress); - metaDAOFactory.setMetaDAOOfficerName(metaDAOOfficerName); - metaDAOFactory.setMetaDAOOfficerContact(metaDAOOfficerContact); - metaDAOFactory.setMetaDAOOfficerTitle(metaDAOOfficerTitle); - - // Create the parent corp (one-time). Reverts if called again. - (address parentCorp, - address parentAuth, - address parentIssuance, - address parentDealMgr, - address parentRoundMgr) = metaDAOFactory.createParentCorp( - bytes32(keccak256("Futarchy Governance SPC")), - "Futarchy Governance SPC", - "segregated portfolio company", - "Cayman Islands", - "market.governed.civilization@metadao.fi", - "binding arbitration", - corpPayable - ); - - // Assign roles and revoke EOA ownership (after setup) - auth.updateRole(address(officerAddress), auth.OWNER_ROLE()); - auth.updateRole(address(corpPayable), auth.OWNER_ROLE()); - - - console.log("Auth:", address(auth)); - console.log("CyberAgreementRegistry:", address(registry)); - console.log("CertificateUriBuilder:", address(uriBuilder)); - console.log("IssuanceManagerFactory:", address(issuanceManagerFactory)); - console.log("CyberCorpSingleFactory:", address(cyberCorpSingleFactory)); - console.log("DealManagerFactory:", address(dealManagerFactory)); - // console.log("RoundManagerFactory:", address(roundManagerFactory)); - //console.log("CyberCertPrinter Impl:", address(cyberCertPrinterImplementation)); - // console.log("CyberScrip Impl:", address(cyberCert20Implementation)); - console.log("MetaDAOFactory (proxy):", address(metaDAOFactory)); - console.log("ParentCorp:", parentCorp); - console.log("ParentAuth:", parentAuth); - console.log("ParentIssuance:", parentIssuance); - console.log("ParentDealMgr:", parentDealMgr); - console.log("NewAgreementRegistryImplementation:", address(newAgreementRegistryImplementation)); - //console.log("ParentRoundMgr:", parentRoundMgr); - - vm.stopBroadcast(); - - return (CyberAgreementRegistry(registry), metaDAOFactory); - } - - function _formatEscrowAgreementTypedDataJson( - CyberAgreementRegistry registry, - string memory contractUri, - string[] memory partyFields, - string[] memory partyValues - ) internal returns (string memory) { - string memory domainSeparatorJson = vm.serializeJsonType( - DOMAIN_SEPARATOR_TYPE, - abi.encode(DomainSeparator({ - name: registry.name(), - version: registry.version(), - chainId: block.chainid, - verifyingContract: address(registry) - })) - ); - - string memory escrowSignatureDataJson = vm.serializeJsonType( - ESCROW_SIGNATUREDATA_TYPE, - abi.encode(EscrowSignatureData({ - legalContractUri: contractUri, - partyFields: partyFields, - partyValues: partyValues - })) - ); - - // Build the json string with the temporary buffer at key "outputKey" - vm.serializeString("outputKey", "domain", domainSeparatorJson); - vm.serializeString("outputKey", "message", escrowSignatureDataJson); - vm.serializeString("outputKey", "primaryType", "EscrowSignatureData"); - return vm.serializeString("outputKey", "types", "{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"EscrowSignatureData\":[{\"name\":\"legalContractUri\",\"type\":\"string\"},{\"name\":\"partyFields\",\"type\":\"string[]\"},{\"name\":\"partyValues\",\"type\":\"string[]\"}]}"); - } -} \ No newline at end of file diff --git a/test/DeployParentCoFactory.t.sol b/test/DeployParentCoFactory.t.sol index 84a87f1..8d33746 100644 --- a/test/DeployParentCoFactory.t.sol +++ b/test/DeployParentCoFactory.t.sol @@ -10,6 +10,7 @@ import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; import {ParentCoFactory} from "../src/ParentCoFactory.sol"; import {CyberCorp} from "../src/CyberCorp.sol"; import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; +import {BorgAuth} from "../src/libs/auth.sol"; contract DeployParentCoFactoryTest is Test { DeploymentConstants.CoreDeployment coreDeployment; @@ -131,6 +132,29 @@ contract DeployParentCoFactoryTest is Test { corp.companyOfficers(2); } + function test_parentCoMultisigIsCorpPayable() public { + CyberCorp corp = CyberCorp(parentCoFactory.parentCorp()); + assertEq(corp.companyPayable(), parentCoMultisig); + } + + function test_corpPayableIsOwnerOfParentCoFactory() public { + uint256 ownerRole = parentCoFactory.AUTH().OWNER_ROLE(); + assertGe(parentCoFactory.userRoles(parentCoMultisig), ownerRole); + } + + function test_parentCoOfficersAreOwnersOfParentCoFactory() public { + uint256 ownerRole = parentCoFactory.AUTH().OWNER_ROLE(); + assertGe(parentCoFactory.userRoles(parentCoOfficer1), ownerRole); + assertGe(parentCoFactory.userRoles(parentCoOfficer2), ownerRole); + } + + function test_parentCoOfficersAreOwnersOfParentCyberCorp() public { + CyberCorp corp = CyberCorp(parentCoFactory.parentCorp()); + uint256 ownerRole = corp.AUTH().OWNER_ROLE(); + assertGe(corp.userRoles(parentCoOfficer1), ownerRole); + assertGe(corp.userRoles(parentCoOfficer2), ownerRole); + } + function test_deploySubCorp_revertIfEscrowSignerNotOfficer() public { (, uint256 nonOfficerKey) = makeAddrAndKey("nonOfficer"); bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(); From e2a150fe0bad87feee25c0a607d78216ca6c75a3 Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 14:27:50 -0700 Subject: [PATCH 08/20] chore: revoke deployer ownership --- script/deploy-parentco-factory.s.sol | 15 ++++++++++++--- test/DeployParentCoFactory.t.sol | 11 ++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index adfe13a..1dd2484 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -58,13 +58,13 @@ contract DeployParentCoFactoryScript is Script { // // CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](2); // parentCoOfficers[0] = CompanyOfficer({ -// eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, +// eoa: 0x8E9603BcB5D974Ed9C870510F3665F67CE5c5bDe, // name: "Test Officer 1", // contact: "test1@parentco.example", // title: "Director" // }); // parentCoOfficers[1] = CompanyOfficer({ -// eoa: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, +// eoa: 0xE8ABA57F64Db674E35fADBb497c3bea4bD69F787, // name: "Test Officer 2", // contact: "test2@parentco.example", // title: "Director" @@ -80,7 +80,7 @@ contract DeployParentCoFactoryScript is Script { // boardConsentTemplateId: keccak256("ParentCo.Test2.BoardConsent.v1"), // boardConsentName: "ParentCo Test Board Consent", // boardConsentUri: "ipfs://parentco-test-board-consent-template", -// parentCoPayable: 0x42069BaBe92462393FaFdc653A88F958B64EC9A3, +// parentCoPayable: 0x8E9603BcB5D974Ed9C870510F3665F67CE5c5bDe, // parentCoName: "Test ParentCo LLC", // parentCoType: "limited liability company", // parentCoJurisdiction: "Delaware", @@ -121,6 +121,7 @@ contract DeployParentCoFactoryScript is Script { bytes32 salt = bytes32(keccak256(bytes(saltStr))); console2.log("==== Configs ===="); + console2.log("deployer: %s", deployerAddress); for (uint256 i = 0; i < parentCoOfficers.length; i++) { console2.log("parentCoOfficers %d:", i); console2.log(" EOA: %s", parentCoOfficers[i].eoa); @@ -129,6 +130,11 @@ contract DeployParentCoFactoryScript is Script { console2.log(" title: %s", parentCoOfficers[i].title); console2.log(""); } + console2.log("UMIA FOUNDER/OPERATOR LEGAL PACK template ID:"); + console2.logBytes32(segCoTemplateId); + console2.log("ACTION BY UNANIMOUS WRITTEN CONSENT OF THE BOARD OF DIRECTORS OF UMIA LAUNCHER SPC template ID:"); + console2.logBytes32(boardConsentTemplateId); + console2.log(""); vm.startBroadcast(deployerPrivateKey); @@ -177,6 +183,9 @@ contract DeployParentCoFactoryScript is Script { parentCoFactoryAuth.updateRole(parentCoOfficers[i].eoa, parentCoFactoryAuth.OWNER_ROLE()); parentCoFactoryAuth.updateRole(parentCoPayable, parentCoFactoryAuth.OWNER_ROLE()); + // Deployer to self-revoke ownership + parentCoFactoryAuth.zeroOwner(); + console2.log("==== Deployed ===="); console2.log("ParentCoFactory Auth:", address(parentCoFactoryAuth)); console2.log( diff --git a/test/DeployParentCoFactory.t.sol b/test/DeployParentCoFactory.t.sol index 8d33746..ef38221 100644 --- a/test/DeployParentCoFactory.t.sol +++ b/test/DeployParentCoFactory.t.sol @@ -18,6 +18,9 @@ contract DeployParentCoFactoryTest is Test { CyberAgreementRegistry registry; + address deployer; + uint256 deployerPrivKey; + ParentCoFactory parentCoFactory; address parentCoMultisig; address parentCoOfficer1; @@ -38,6 +41,8 @@ contract DeployParentCoFactoryTest is Test { registry = CyberAgreementRegistry(coreDeployment.cyberAgreementRegistry); + (deployer, deployerPrivKey) = makeAddrAndKey("deployer"); + (parentCoMultisig, ) = makeAddrAndKey("parentCoMultisig"); (parentCoOfficer1, parentCoOfficer1PrivKey) = makeAddrAndKey("parentCoOfficer1"); (parentCoOfficer2, parentCoOfficer2PrivKey) = makeAddrAndKey("parentCoOfficer2"); @@ -61,7 +66,7 @@ contract DeployParentCoFactoryTest is Test { GnosisTransaction[] memory safeTxs; (parentCoFactory, safeTxs) = (new DeployParentCoFactoryScript()).runWithArgs({ chainId: block.chainid, - deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), + deployerPrivateKey: deployerPrivKey, saltStr: "ParentCoFactory.deploy.v1", segCoTemplateId: segCoTemplateId, segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", @@ -132,6 +137,10 @@ contract DeployParentCoFactoryTest is Test { corp.companyOfficers(2); } + function test_deployerHasSelfRevokedOwnerOfParentCoFactory() public { + assertEq(parentCoFactory.userRoles(deployer), 0); + } + function test_parentCoMultisigIsCorpPayable() public { CyberCorp corp = CyberCorp(parentCoFactory.parentCorp()); assertEq(corp.companyPayable(), parentCoMultisig); From 3f9fe770e3f4c9fa25dc4ab5162ad968a14746bb Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 16:55:48 -0700 Subject: [PATCH 09/20] test: increase sub-corp deployment test coverage --- test/DeployParentCoFactory.t.sol | 449 ++++++++++++++++++++++++++++--- 1 file changed, 411 insertions(+), 38 deletions(-) diff --git a/test/DeployParentCoFactory.t.sol b/test/DeployParentCoFactory.t.sol index ef38221..949d308 100644 --- a/test/DeployParentCoFactory.t.sol +++ b/test/DeployParentCoFactory.t.sol @@ -35,6 +35,15 @@ contract DeployParentCoFactoryTest is Test { address subCorpOfficer; uint256 subCorpOfficerPrivKey; + struct SubCorpResult { + address subCorp; + address subAuth; + address subIssuance; + address subDealMgr; + bytes32 expectedSegCoId; + bytes32 expectedBoardConsentId; + } + function setUp() public { coreDeployment = DeploymentConstants.coreV2(block.chainid); deps = DeploymentConstants.deps(block.chainid); @@ -91,10 +100,96 @@ contract DeployParentCoFactoryTest is Test { } // Set escrow sig: parentCoOfficer1 signs the factory-specific EIP-712 authorization - bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(parentCoOfficer1PrivKey, escrowDigest); + bytes memory escrowSig = _createParentCoSignatureHash(parentCoOfficer1PrivKey); vm.prank(parentCoOfficer1); - parentCoFactory.setParentCoSignatureHash(abi.encodePacked(r, s, v)); + parentCoFactory.setParentCoSignatureHash(escrowSig); + } + + function _deploySubCorp() internal returns (SubCorpResult memory r) { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + string memory subCompanyName = "Test SubCo SPV 1"; + string memory subCompanyType = "series limited liability company"; + string memory subCompanyJurisdiction = "Delaware"; + string memory subCompanyContact = "subco@parentco.example"; + string memory subDisputeResolution = "binding arbitration"; + + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = subCompanyName; + globalValues[3] = subCompanyType; + globalValues[4] = subCompanyJurisdiction; + globalValues[5] = subCompanyContact; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: partyValues[0], + contact: partyValues[1], + title: "Founder" + }); + + // Pre-compute expected agreement IDs (registry: keccak256(abi.encode(templateId, salt, globalValues, parties))) + // parentCoOfficer1 is the escrow signer set in setUp + address[] memory segCoParties = new address[](2); + segCoParties[0] = parentCoOfficer1; + segCoParties[1] = subCorpOfficer; + r.expectedSegCoId = keccak256(abi.encode(segCoTemplateId, subCorpSalt, globalValues, segCoParties)); + + address[] memory boardConsentParties = new address[](1); + boardConsentParties[0] = parentCoOfficer1; + r.expectedBoardConsentId = keccak256(abi.encode(boardConsentTemplateId, subCorpSalt, globalValues, boardConsentParties)); + + ( + string memory legalContractUri, + , + string[] memory templateGlobalFields, + string[] memory templatePartyFields + ) = registry.getTemplateDetails(segCoTemplateId); + + bytes memory subCorpOwnerSignature = CyberAgreementUtils.signAgreementTypedData( + vm, + registry.DOMAIN_SEPARATOR(), + registry.SIGNATUREDATA_TYPEHASH(), + r.expectedSegCoId, + legalContractUri, + templateGlobalFields, + templatePartyFields, + globalValues, + partyValues, + subCorpOfficerPrivKey + ); + + ( + r.subCorp, + r.subAuth, + r.subIssuance, + r.subDealMgr, + , + , + , + + ) = parentCoFactory.deployCorpContractFor( + subCorpSalt, + subCompanyName, + subCompanyType, + subCompanyJurisdiction, + subCompanyContact, + subDisputeResolution, + subCorpPayable, + subOfficer, + segCoTemplateId, + boardConsentTemplateId, + globalValues, + partyValues, + subCorpOwnerSignature, + subCorpOfficer + ); } function test_parentCoOfficersInFactory() public { @@ -166,10 +261,9 @@ contract DeployParentCoFactoryTest is Test { function test_deploySubCorp_revertIfEscrowSignerNotOfficer() public { (, uint256 nonOfficerKey) = makeAddrAndKey("nonOfficer"); - bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(nonOfficerKey, escrowDigest); + bytes memory nonOfficerSig = _createParentCoSignatureHash(nonOfficerKey); vm.prank(parentCoOfficer1); - parentCoFactory.setParentCoSignatureHash(abi.encodePacked(r, s, v)); + parentCoFactory.setParentCoSignatureHash(nonOfficerSig); CompanyOfficer memory dummyOfficer = CompanyOfficer({eoa: address(0), name: "", contact: "", title: ""}); string[] memory empty = new string[](0); @@ -200,9 +294,61 @@ contract DeployParentCoFactoryTest is Test { ); } - // TODO WIP: verify results - function test_deploySubCorp() public { - // Build SubCorp inputs matching ParentCoFactory's strict field checks. + function test_deploySubCorp_returnsNonZeroAddresses() public { + SubCorpResult memory r = _deploySubCorp(); + assertTrue(r.subCorp != address(0)); + assertTrue(r.subAuth != address(0)); + assertTrue(r.subIssuance != address(0)); + assertTrue(r.subDealMgr != address(0)); + assertTrue(r.subCorp != r.subAuth); + assertTrue(r.subCorp != r.subIssuance); + assertTrue(r.subCorp != r.subDealMgr); + } + + function test_deploySubCorp_subCorpOfficerCorrect() public { + SubCorpResult memory r = _deploySubCorp(); + CyberCorp corp = CyberCorp(r.subCorp); + + (address eoa, string memory name, string memory contact, string memory title) = corp.companyOfficers(0); + assertEq(eoa, subCorpOfficer); + assertEq(name, "Test Deployer Officer"); + assertEq(contact, "deployer@parentco.example"); + assertEq(title, "Founder"); + assertTrue(corp.isCyberCORPOfficer(subCorpOfficer)); + } + + function test_deploySubCorp_subCorpPayableCorrect() public { + SubCorpResult memory r = _deploySubCorp(); + assertEq(CyberCorp(r.subCorp).companyPayable(), subCorpPayable); + } + + function test_deploySubCorp_subCorpAuthRoles() public { + SubCorpResult memory r = _deploySubCorp(); + BorgAuth subAuth = BorgAuth(r.subAuth); + uint256 ownerRole = subAuth.OWNER_ROLE(); + assertGe(subAuth.userRoles(subCorpOfficer), ownerRole); + } + + function test_deploySubCorp_agreementSignedByBothParties() public { + SubCorpResult memory r = _deploySubCorp(); + assertTrue(registry.hasSigned(r.expectedSegCoId, parentCoOfficer1)); + assertTrue(registry.hasSigned(r.expectedSegCoId, subCorpOfficer)); + assertTrue(registry.allPartiesSigned(r.expectedSegCoId)); + } + + function test_deploySubCorp_boardConsentSigned() public { + SubCorpResult memory r = _deploySubCorp(); + assertTrue(registry.hasSigned(r.expectedBoardConsentId, parentCoOfficer1)); + address[] memory parties = registry.getParties(r.expectedBoardConsentId); + assertEq(parties.length, 1); + assertEq(parties[0], parentCoOfficer1); + } + + function test_deploySubCorp_officer2CanSetEscrowAndDeploy() public { + bytes memory sig = _createParentCoSignatureHash(parentCoOfficer2PrivKey); + vm.prank(parentCoOfficer2); + parentCoFactory.setParentCoSignatureHash(sig); + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); string memory subCompanyName = "Test SubCo SPV 1"; string memory subCompanyType = "series limited liability company"; @@ -231,13 +377,11 @@ contract DeployParentCoFactoryTest is Test { title: "Founder" }); - // Pre-compute agreement id and signer signature expected by signContractFor. - address[] memory agreementParties = new address[](2); - agreementParties[0] = parentCoOfficer1; - agreementParties[1] = subCorpOfficer; - bytes32 agreementId = keccak256( - abi.encode(segCoTemplateId, subCorpSalt, globalValues, agreementParties) - ); + // With officer2 as escrow signer, party[0] = parentCoOfficer2 + address[] memory segCoParties = new address[](2); + segCoParties[0] = parentCoOfficer2; + segCoParties[1] = subCorpOfficer; + bytes32 agreementId = keccak256(abi.encode(segCoTemplateId, subCorpSalt, globalValues, segCoParties)); ( string memory legalContractUri, @@ -246,7 +390,7 @@ contract DeployParentCoFactoryTest is Test { string[] memory templatePartyFields ) = registry.getTemplateDetails(segCoTemplateId); - bytes memory subCorpOwnerSignature = CyberAgreementUtils.signAgreementTypedData( + bytes memory agreementSig = CyberAgreementUtils.signAgreementTypedData( vm, registry.DOMAIN_SEPARATOR(), registry.SIGNATUREDATA_TYPEHASH(), @@ -259,16 +403,7 @@ contract DeployParentCoFactoryTest is Test { subCorpOfficerPrivKey ); - ( - address subCorp, - address subAuth, - address subIssuance, - address subDealMgr, - address subRoundMgr, - address[] memory subCertPrinters, - bytes32 subAgreementId, - uint256[] memory subCertIds - ) = parentCoFactory.deployCorpContractFor( + (address subCorp,,,,,, bytes32 retId,) = parentCoFactory.deployCorpContractFor( subCorpSalt, subCompanyName, subCompanyType, @@ -281,19 +416,257 @@ contract DeployParentCoFactoryTest is Test { boardConsentTemplateId, globalValues, partyValues, - subCorpOwnerSignature, + agreementSig, subCorpOfficer ); - console2.log("SubCorp:", subCorp); - console2.log("SubAuth:", subAuth); - console2.log("SubIssuance:", subIssuance); - console2.log("SubDealMgr:", subDealMgr); - console2.log("SubRoundMgr:", subRoundMgr); - console2.log("SubAgreementId:"); - console2.logBytes32(subAgreementId); - console2.log("SubCertPrinters count:", subCertPrinters.length); - console2.log("SubCertIds count:", subCertIds.length); - console2.log(""); + assertTrue(subCorp != address(0)); + assertTrue(registry.hasSigned(agreementId, parentCoOfficer2)); + assertTrue(registry.hasSigned(agreementId, subCorpOfficer)); + } + + function test_deploySubCorp_revertIfPartyValuesTooShort() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "Test SubCo SPV 1"; + globalValues[3] = "series limited liability company"; + globalValues[4] = "Delaware"; + globalValues[5] = "subco@parentco.example"; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory shortPartyValues = new string[](1); + shortPartyValues[0] = "Test Deployer Officer"; + + vm.expectRevert(ParentCoFactory.GlobalOrPartyValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, shortPartyValues, hex"", subCorpOfficer + ); + } + + function test_deploySubCorp_revertIfOfficerNameMismatch() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "Test SubCo SPV 1"; + globalValues[3] = "series limited liability company"; + globalValues[4] = "Delaware"; + globalValues[5] = "subco@parentco.example"; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory partyValues = new string[](2); + partyValues[0] = "WRONG NAME"; // != officer.name + partyValues[1] = "deployer@parentco.example"; + + vm.expectRevert(ParentCoFactory.GlobalOrPartyValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, partyValues, hex"", subCorpOfficer + ); + } + + function test_deploySubCorp_revertIfOfficerContactMismatch() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "Test SubCo SPV 1"; + globalValues[3] = "series limited liability company"; + globalValues[4] = "Delaware"; + globalValues[5] = "subco@parentco.example"; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "wrong@email.com"; // != officer.contact + + vm.expectRevert(ParentCoFactory.GlobalOrPartyValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, partyValues, hex"", subCorpOfficer + ); + } + + function test_deploySubCorp_revertIfCompanyNameMismatch() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "WRONG COMPANY NAME"; // != companyName arg below + globalValues[3] = "series limited liability company"; + globalValues[4] = "Delaware"; + globalValues[5] = "subco@parentco.example"; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + vm.expectRevert(ParentCoFactory.GlobalOrPartyValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, partyValues, hex"", subCorpOfficer + ); + } + + function test_deploySubCorp_revertIfCompanyTypeMismatch() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "Test SubCo SPV 1"; + globalValues[3] = "WRONG TYPE"; // != companyType arg below + globalValues[4] = "Delaware"; + globalValues[5] = "subco@parentco.example"; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + vm.expectRevert(ParentCoFactory.GlobalOrPartyValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, partyValues, hex"", subCorpOfficer + ); + } + + function test_deploySubCorp_revertIfJurisdictionMismatch() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "Test SubCo SPV 1"; + globalValues[3] = "series limited liability company"; + globalValues[4] = "Nevada"; // != companyJurisdiction arg below + globalValues[5] = "subco@parentco.example"; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + vm.expectRevert(ParentCoFactory.GlobalOrPartyValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, partyValues, hex"", subCorpOfficer + ); + } + + function test_deploySubCorp_revertIfContactDetailsMismatch() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "Test SubCo SPV 1"; + globalValues[3] = "series limited liability company"; + globalValues[4] = "Delaware"; + globalValues[5] = "wrong@contact.com"; // != companyContactDetails arg below + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + vm.expectRevert(ParentCoFactory.GlobalOrPartyValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, partyValues, hex"", subCorpOfficer + ); + } + + function test_deploySubCorp_revertIfOfficerEoaMismatch() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: address(1), // != deployer arg below + name: "Test Deployer Officer", + contact: "deployer@parentco.example", + title: "Founder" + }); + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = "Test SubCo SPV 1"; + globalValues[3] = "series limited liability company"; + globalValues[4] = "Delaware"; + globalValues[5] = "subco@parentco.example"; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + vm.expectRevert(ParentCoFactory.OfficerValuesMismatch.selector); + parentCoFactory.deployCorpContractFor( + subCorpSalt, "Test SubCo SPV 1", "series limited liability company", + "Delaware", "subco@parentco.example", "binding arbitration", + subCorpPayable, subOfficer, segCoTemplateId, boardConsentTemplateId, + globalValues, partyValues, hex"", subCorpOfficer + ); + } + + function _createParentCoSignatureHash(uint256 privKey) internal returns (bytes memory) { + bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(); + (uint8 v, bytes32 r_, bytes32 s) = vm.sign(privKey, escrowDigest); + return abi.encodePacked(r_, s, v); } } From 204a3ac40506cbfb72f3df254513ee5bc0cc16fb Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 19:57:23 -0700 Subject: [PATCH 10/20] feat: add officer details to escrow authorization hash --- src/ParentCoFactory.sol | 20 +++++++++--- test/DeployParentCoFactory.t.sol | 52 ++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/ParentCoFactory.sol b/src/ParentCoFactory.sol index 19007f5..d048754 100644 --- a/src/ParentCoFactory.sol +++ b/src/ParentCoFactory.sol @@ -193,7 +193,7 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { address(this) ) ); - ESCROW_AUTHORIZATION_TYPEHASH = keccak256("EscrowAuthorization(address factory)"); + ESCROW_AUTHORIZATION_TYPEHASH = keccak256("EscrowAuthorization(string name,string contact)"); } function setParentCoSignatureHash(bytes memory _parentCoSignatureHash) public onlyOwner { @@ -210,12 +210,21 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { parentCoOfficers.push(_officers[i]); } - function escrowAuthorizationHash() public view returns (bytes32) { + /// @notice Authorization should include everything used to escrow-sign: + /// - escrow contract (implicit) + /// - EOA (implicit) + /// - name (explicit) + /// - contact (explicit) + function escrowAuthorizationHash(string memory name, string memory contact) public view returns (bytes32) { return keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, - keccak256(abi.encode(ESCROW_AUTHORIZATION_TYPEHASH, address(this))) + keccak256(abi.encode( + ESCROW_AUTHORIZATION_TYPEHASH, + keccak256(bytes(name)), + keccak256(bytes(contact)) + )) ) ); } @@ -420,10 +429,13 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { ) { // Resolve which officer pre-signed the escrow authorization - address escrowSigner = escrowAuthorizationHash().recover(parentCoSignatureHash); CompanyOfficer memory signingOfficer; bool signerFound; for (uint256 i = 0; i < parentCoOfficers.length; i++) { + address escrowSigner = escrowAuthorizationHash( + parentCoOfficers[i].name, + parentCoOfficers[i].contact + ).recover(parentCoSignatureHash); if (parentCoOfficers[i].eoa == escrowSigner) { signingOfficer = parentCoOfficers[i]; signerFound = true; diff --git a/test/DeployParentCoFactory.t.sol b/test/DeployParentCoFactory.t.sol index 949d308..cdca630 100644 --- a/test/DeployParentCoFactory.t.sol +++ b/test/DeployParentCoFactory.t.sol @@ -28,6 +28,8 @@ contract DeployParentCoFactoryTest is Test { address parentCoOfficer2; uint256 parentCoOfficer2PrivKey; + CompanyOfficer[] parentCoOfficers; + bytes32 segCoTemplateId = keccak256("ParentCo.SegCo.v1"); bytes32 boardConsentTemplateId = keccak256("ParentCo.BoardConsent.v1"); @@ -58,19 +60,18 @@ contract DeployParentCoFactoryTest is Test { (subCorpPayable, ) = makeAddrAndKey("subCorpPayable"); (subCorpOfficer, subCorpOfficerPrivKey) = makeAddrAndKey("subCorpOfficer"); - CompanyOfficer[] memory parentCoOfficers = new CompanyOfficer[](2); - parentCoOfficers[0] = CompanyOfficer({ + parentCoOfficers.push(CompanyOfficer({ eoa: parentCoOfficer1, name: "Test ParentCo Officer 1", contact: "test1@parentco.example", title: "Director" - }); - parentCoOfficers[1] = CompanyOfficer({ + })); + parentCoOfficers.push(CompanyOfficer({ eoa: parentCoOfficer2, name: "Test ParentCo Officer 2", contact: "test2@parentco.example", title: "Director" - }); + })); GnosisTransaction[] memory safeTxs; (parentCoFactory, safeTxs) = (new DeployParentCoFactoryScript()).runWithArgs({ @@ -100,7 +101,11 @@ contract DeployParentCoFactoryTest is Test { } // Set escrow sig: parentCoOfficer1 signs the factory-specific EIP-712 authorization - bytes memory escrowSig = _createParentCoSignatureHash(parentCoOfficer1PrivKey); + bytes memory escrowSig = _createParentCoSignatureHash( + parentCoOfficer1PrivKey, + parentCoOfficers[0].name, + parentCoOfficers[0].contact + ); vm.prank(parentCoOfficer1); parentCoFactory.setParentCoSignatureHash(escrowSig); } @@ -261,7 +266,11 @@ contract DeployParentCoFactoryTest is Test { function test_deploySubCorp_revertIfEscrowSignerNotOfficer() public { (, uint256 nonOfficerKey) = makeAddrAndKey("nonOfficer"); - bytes memory nonOfficerSig = _createParentCoSignatureHash(nonOfficerKey); + bytes memory nonOfficerSig = _createParentCoSignatureHash( + nonOfficerKey, + parentCoOfficers[0].name, + parentCoOfficers[0].contact + ); vm.prank(parentCoOfficer1); parentCoFactory.setParentCoSignatureHash(nonOfficerSig); @@ -274,6 +283,25 @@ contract DeployParentCoFactoryTest is Test { ); } + function test_deploySubCorp_revertIfOfficerSignsWithAnotherOfficersIdentity() public { + // officer2 signs using officer1's name+contact — should not authenticate as officer1 + bytes memory spoofedSig = _createParentCoSignatureHash( + parentCoOfficer2PrivKey, + parentCoOfficers[0].name, + parentCoOfficers[0].contact + ); + vm.prank(parentCoOfficer1); + parentCoFactory.setParentCoSignatureHash(spoofedSig); + + CompanyOfficer memory dummyOfficer = CompanyOfficer({eoa: address(0), name: "", contact: "", title: ""}); + string[] memory empty = new string[](0); + vm.expectRevert(ParentCoFactory.UnauthorizedEscrowSigner.selector); + parentCoFactory.deployCorpContractFor( + 0, "", "", "", "", "", address(0), dummyOfficer, + bytes32(0), bytes32(0), empty, empty, hex"", address(0) + ); + } + function test_deploySubCorp_revertIfEscrowSigForWrongFactory() public { // Officer signs a valid EIP-712 sig but with address(0) as the factory field bytes32 wrongDigest = keccak256(abi.encodePacked( @@ -345,7 +373,11 @@ contract DeployParentCoFactoryTest is Test { } function test_deploySubCorp_officer2CanSetEscrowAndDeploy() public { - bytes memory sig = _createParentCoSignatureHash(parentCoOfficer2PrivKey); + bytes memory sig = _createParentCoSignatureHash( + parentCoOfficer2PrivKey, + parentCoOfficers[1].name, + parentCoOfficers[1].contact + ); vm.prank(parentCoOfficer2); parentCoFactory.setParentCoSignatureHash(sig); @@ -664,8 +696,8 @@ contract DeployParentCoFactoryTest is Test { ); } - function _createParentCoSignatureHash(uint256 privKey) internal returns (bytes memory) { - bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(); + function _createParentCoSignatureHash(uint256 privKey, string memory name, string memory contact) internal returns (bytes memory) { + bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(name, contact); (uint8 v, bytes32 r_, bytes32 s) = vm.sign(privKey, escrowDigest); return abi.encodePacked(r_, s, v); } From 445ab9dc9d90342dc849cf46c68ad37611da663f Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 21:07:33 -0700 Subject: [PATCH 11/20] chore: deploy new staging --- script/deploy-parentco-factory.s.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index 1dd2484..cccd70a 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -73,7 +73,7 @@ contract DeployParentCoFactoryScript is Script { // return runWithArgs({ // chainId: DeploymentConstants.BASE_SEPOLIA, // deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), -// saltStr: "ParentCoFactory.deploy.v2", +// saltStr: "ParentCoFactory.deploy.v2.dev0", // segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), // segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", // segCoDocUri: "ipfs://parentco-test-segco-template", @@ -81,7 +81,7 @@ contract DeployParentCoFactoryScript is Script { // boardConsentName: "ParentCo Test Board Consent", // boardConsentUri: "ipfs://parentco-test-board-consent-template", // parentCoPayable: 0x8E9603BcB5D974Ed9C870510F3665F67CE5c5bDe, -// parentCoName: "Test ParentCo LLC", +// parentCoName: "Test ParentCo LLC dev0", // parentCoType: "limited liability company", // parentCoJurisdiction: "Delaware", // parentCoContactDetails: "test@parentco.example", @@ -122,6 +122,7 @@ contract DeployParentCoFactoryScript is Script { console2.log("==== Configs ===="); console2.log("deployer: %s", deployerAddress); + console2.log("saltStr: %s", saltStr); for (uint256 i = 0; i < parentCoOfficers.length; i++) { console2.log("parentCoOfficers %d:", i); console2.log(" EOA: %s", parentCoOfficers[i].eoa); From b1c6b72127ea0a8d38fae27851df4eb809afa57a Mon Sep 17 00:00:00 2001 From: detoo Date: Mon, 27 Apr 2026 21:31:48 -0700 Subject: [PATCH 12/20] test: acceptance test for staging --- script/libs/DeploymentConstants.sol | 22 ++++ test/DeployParentCoFactoryAcceptance.t.sol | 138 +++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 test/DeployParentCoFactoryAcceptance.t.sol diff --git a/script/libs/DeploymentConstants.sol b/script/libs/DeploymentConstants.sol index 106f6fd..f42f92b 100644 --- a/script/libs/DeploymentConstants.sol +++ b/script/libs/DeploymentConstants.sol @@ -26,6 +26,12 @@ library DeploymentConstants { address lexchexCondition; } + struct UmiaDeployment { + address parentCoFactory; + bytes32 segCoTemplateId; + bytes32 boardConsentTemplateId; + } + struct Deps { address usdc; } @@ -75,6 +81,22 @@ library DeploymentConstants { } } + function umia(uint256 chainId) + internal + pure + returns (UmiaDeployment memory deployment) + { + if (chainId == BASE_SEPOLIA) { + return UmiaDeployment({ + parentCoFactory: 0x478ee34c618E9339Ae2DD8100Df7ec535eb24D29, + segCoTemplateId: 0xb6da5c8e53767592c0eeb4c5c0d77eae7e1e2e795190e7237d837b3fbc98ed75, + boardConsentTemplateId: 0xc02175e98621a996529fb751b30e0b7a8344ece3b00f46a29c1e904c9da87a46 + }); + } else { + revert UnsupportedChain(chainId); + } + } + function deps(uint256 chainId) internal pure diff --git a/test/DeployParentCoFactoryAcceptance.t.sol b/test/DeployParentCoFactoryAcceptance.t.sol new file mode 100644 index 0000000..7101caf --- /dev/null +++ b/test/DeployParentCoFactoryAcceptance.t.sol @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {Test, Vm, console2} from "forge-std/Test.sol"; +import {CyberAgreementUtils} from "./libs/CyberAgreementUtils.sol"; +import {DeployParentCoFactoryScript} from "../script/deploy-parentco-factory.s.sol"; +import {GnosisTransaction} from "../script/libs/safe.sol"; +import {DeploymentConstants} from "../script/libs/DeploymentConstants.sol"; +import {CyberAgreementRegistry} from "../src/CyberAgreementRegistry.sol"; +import {ParentCoFactory} from "../src/ParentCoFactory.sol"; +import {CyberCorp} from "../src/CyberCorp.sol"; +import {CompanyOfficer} from "../src/CyberCorpConstants.sol"; +import {BorgAuth} from "../src/libs/auth.sol"; + +contract DeployParentCoFactoryAcceptanceTest is Test { + DeploymentConstants.CoreDeployment coreDeployment; + DeploymentConstants.UmiaDeployment umiaDeployment; + DeploymentConstants.Deps deps; + + CyberAgreementRegistry registry; + + ParentCoFactory parentCoFactory; + CompanyOfficer[] parentCoOfficers; + + address subCorpPayable; + address subCorpOfficer; + uint256 subCorpOfficerPrivKey; + + /// @notice Assumes all contracts are deployed and escrow signature has been submitted + function setUp() public { + coreDeployment = DeploymentConstants.coreV2(block.chainid); + umiaDeployment = DeploymentConstants.umia(block.chainid); + deps = DeploymentConstants.deps(block.chainid); + + (subCorpPayable, ) = makeAddrAndKey("subCorpPayable"); + (subCorpOfficer, subCorpOfficerPrivKey) = makeAddrAndKey("subCorpOfficer"); + + registry = CyberAgreementRegistry(coreDeployment.cyberAgreementRegistry); + + parentCoFactory = ParentCoFactory(umiaDeployment.parentCoFactory); + + for(uint256 i = 0; true ; i++) { + try parentCoFactory.parentCoOfficers(i) returns (address eoa, string memory name, string memory contact, string memory title) { + parentCoOfficers.push(CompanyOfficer({ + eoa: eoa, + name: name, + contact: contact, + title: title + })); + } catch { + break; + } + } + } + + function test_deploySubCorp() public { + uint256 subCorpSalt = uint256(keccak256("ParentCo.Test2.SubCorp.v1")); + string memory subCompanyName = "Test SubCo SPV 1"; + string memory subCompanyType = "series limited liability company"; + string memory subCompanyJurisdiction = "Delaware"; + string memory subCompanyContact = "subco@parentco.example"; + string memory subDisputeResolution = "binding arbitration"; + + string[] memory globalValues = new string[](8); + globalValues[0] = "Test Founder"; + globalValues[1] = "Test ParentCo Enterprise"; + globalValues[2] = subCompanyName; + globalValues[3] = subCompanyType; + globalValues[4] = subCompanyJurisdiction; + globalValues[5] = subCompanyContact; + globalValues[6] = "TSC1"; + globalValues[7] = "Test SubCo One"; + + string[] memory partyValues = new string[](2); + partyValues[0] = "Test Deployer Officer"; + partyValues[1] = "deployer@parentco.example"; + + CompanyOfficer memory subOfficer = CompanyOfficer({ + eoa: subCorpOfficer, + name: partyValues[0], + contact: partyValues[1], + title: "Founder" + }); + + // With officer2 as escrow signer, party[0] = parentCoOfficer2 + address[] memory segCoParties = new address[](2); + segCoParties[0] = parentCoOfficers[0].eoa; + segCoParties[1] = subCorpOfficer; + bytes32 agreementId = keccak256(abi.encode(umiaDeployment.segCoTemplateId, subCorpSalt, globalValues, segCoParties)); + + ( + string memory legalContractUri, + , + string[] memory templateGlobalFields, + string[] memory templatePartyFields + ) = registry.getTemplateDetails(umiaDeployment.segCoTemplateId); + + bytes memory agreementSig = CyberAgreementUtils.signAgreementTypedData( + vm, + registry.DOMAIN_SEPARATOR(), + registry.SIGNATUREDATA_TYPEHASH(), + agreementId, + legalContractUri, + templateGlobalFields, + templatePartyFields, + globalValues, + partyValues, + subCorpOfficerPrivKey + ); + + (address subCorp,,,,,, bytes32 retId,) = parentCoFactory.deployCorpContractFor( + subCorpSalt, + subCompanyName, + subCompanyType, + subCompanyJurisdiction, + subCompanyContact, + subDisputeResolution, + subCorpPayable, + subOfficer, + umiaDeployment.segCoTemplateId, + umiaDeployment.boardConsentTemplateId, + globalValues, + partyValues, + agreementSig, + subCorpOfficer + ); + + assertTrue(subCorp != address(0)); + assertTrue(registry.hasSigned(agreementId, parentCoOfficers[0].eoa)); + assertTrue(registry.hasSigned(agreementId, subCorpOfficer)); + } + + function _createParentCoSignatureHash(uint256 privKey, string memory name, string memory contact) internal returns (bytes memory) { + bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(name, contact); + (uint8 v, bytes32 r_, bytes32 s) = vm.sign(privKey, escrowDigest); + return abi.encodePacked(r_, s, v); + } +} From ac60c62ada6f9ecac58e73b50c32391d17001cca Mon Sep 17 00:00:00 2001 From: detoo Date: Tue, 28 Apr 2026 14:02:22 -0700 Subject: [PATCH 13/20] test: more real-world test cases for staging --- test/DeployParentCoFactoryAcceptance.t.sol | 48 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/test/DeployParentCoFactoryAcceptance.t.sol b/test/DeployParentCoFactoryAcceptance.t.sol index 7101caf..79284ed 100644 --- a/test/DeployParentCoFactoryAcceptance.t.sol +++ b/test/DeployParentCoFactoryAcceptance.t.sol @@ -108,7 +108,7 @@ contract DeployParentCoFactoryAcceptanceTest is Test { subCorpOfficerPrivKey ); - (address subCorp,,,,,, bytes32 retId,) = parentCoFactory.deployCorpContractFor( + (address subCorp,,,,,,,) = parentCoFactory.deployCorpContractFor( subCorpSalt, subCompanyName, subCompanyType, @@ -130,6 +130,52 @@ contract DeployParentCoFactoryAcceptanceTest is Test { assertTrue(registry.hasSigned(agreementId, subCorpOfficer)); } + function test_realCalldata() public { + // Skip test if not on Base Sepolia + if(block.chainid != DeploymentConstants.BASE_SEPOLIA) { + console2.log("skipping unsupported chain ID: %d ...", block.chainid); + return; + } + + // https://sepolia.basescan.org/tx/0xd479c8e723bd1999945477fdb7fe9345f488c248b3e02731c050e442aba5d827 + vm.rollFork(40820661); // one block before the real tx + + string[] memory globalValues = new string[](8); + globalValues[0] = "alice"; + globalValues[1] = "TestCorp"; + globalValues[2] = "TestCorp S.P."; + globalValues[3] = "Segregated Portfolio of Segregated Portfolio Company"; + globalValues[4] = "Cayman Islands"; + + string[] memory partyValues = new string[](2); + partyValues[0] = "alice"; + partyValues[1] = "123"; + + (address subCorp,,,,,,,) = parentCoFactory.deployCorpContractFor({ + salt: 1777409598760, + companyName: "TestCorp S.P.", + companyType: "Segregated Portfolio of Segregated Portfolio Company", + companyJurisdiction: "Cayman Islands", + companyContactDetails: "", + defaultDisputeResolution: "", + _companyPayable: 0x0000000000000000000000000000000000000000, + _officer: CompanyOfficer({ + eoa: 0xd0c3D2b2D19854036a22aFB386920854A67DFC10, + name: "alice", + contact: "123", + title: "Operator" + }), + _segCoTemplateId: 0xb6da5c8e53767592c0eeb4c5c0d77eae7e1e2e795190e7237d837b3fbc98ed75, + _boardConsentTempateId: 0xc02175e98621a996529fb751b30e0b7a8344ece3b00f46a29c1e904c9da87a46, + _globalValues: globalValues, + _partyValues: partyValues, + signature: hex"cf3cd32652fc7998ac55b7c2f457a39519093d28bd0a42c8773f2755ea0ace7831eeb19883a81d501889402f28924a526212155922498b71ae5a109590705b7d1b", + deployer: 0xd0c3D2b2D19854036a22aFB386920854A67DFC10 + }); + + assertTrue(subCorp != address(0)); + } + function _createParentCoSignatureHash(uint256 privKey, string memory name, string memory contact) internal returns (bytes memory) { bytes32 escrowDigest = parentCoFactory.escrowAuthorizationHash(name, contact); (uint8 v, bytes32 r_, bytes32 s) = vm.sign(privKey, escrowDigest); From 236b9b6b6589e4e63257ed00baa8f5142b983f0c Mon Sep 17 00:00:00 2001 From: detoo Date: Wed, 29 Apr 2026 11:09:37 -0700 Subject: [PATCH 14/20] chore: deploy to production --- script/libs/DeploymentConstants.sol | 8 +++- test/DeployParentCoFactoryAcceptance.t.sol | 54 +++++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/script/libs/DeploymentConstants.sol b/script/libs/DeploymentConstants.sol index f42f92b..8c8d241 100644 --- a/script/libs/DeploymentConstants.sol +++ b/script/libs/DeploymentConstants.sol @@ -86,7 +86,13 @@ library DeploymentConstants { pure returns (UmiaDeployment memory deployment) { - if (chainId == BASE_SEPOLIA) { + if (chainId == BASE) { + return UmiaDeployment({ + parentCoFactory: 0x50512F70A37C90C9AFCe2074AEA5E9F99Dd21Df1, + segCoTemplateId: 0xd9e0fbb89f8e4e973f05d6b40b6a41e3a9af845b604e9acc7aa4f2a0c37009d8, + boardConsentTemplateId: 0x93ac1365e39b1d8237c84cf969b752ffbb717f7d8144eb47562b4060bcd91c30 + }); + } else if (chainId == BASE_SEPOLIA) { return UmiaDeployment({ parentCoFactory: 0x478ee34c618E9339Ae2DD8100Df7ec535eb24D29, segCoTemplateId: 0xb6da5c8e53767592c0eeb4c5c0d77eae7e1e2e795190e7237d837b3fbc98ed75, diff --git a/test/DeployParentCoFactoryAcceptance.t.sol b/test/DeployParentCoFactoryAcceptance.t.sol index 79284ed..dfa4654 100644 --- a/test/DeployParentCoFactoryAcceptance.t.sol +++ b/test/DeployParentCoFactoryAcceptance.t.sol @@ -22,16 +22,21 @@ contract DeployParentCoFactoryAcceptanceTest is Test { ParentCoFactory parentCoFactory; CompanyOfficer[] parentCoOfficers; + address parentCorpTestOfficer; + uint256 parentCorpTestOfficerPrivKey; + address subCorpPayable; address subCorpOfficer; uint256 subCorpOfficerPrivKey; - /// @notice Assumes all contracts are deployed and escrow signature has been submitted + /// @notice Assumes all contracts are deployed function setUp() public { coreDeployment = DeploymentConstants.coreV2(block.chainid); umiaDeployment = DeploymentConstants.umia(block.chainid); deps = DeploymentConstants.deps(block.chainid); + (parentCorpTestOfficer, parentCorpTestOfficerPrivKey) = makeAddrAndKey("parentCorpTestOfficer"); + (subCorpPayable, ) = makeAddrAndKey("subCorpPayable"); (subCorpOfficer, subCorpOfficerPrivKey) = makeAddrAndKey("subCorpOfficer"); @@ -39,8 +44,38 @@ contract DeployParentCoFactoryAcceptanceTest is Test { parentCoFactory = ParentCoFactory(umiaDeployment.parentCoFactory); + // Simulate MetaLeX creating the templates + // TODO comment out after MetaLeX safe executed the safe txs + + GnosisTransaction[] memory safeTxs = new GnosisTransaction[](2); + safeTxs[0] = GnosisTransaction({ + to: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, + value: 0, + data: hex"55f0c0c6d9e0fbb89f8e4e973f05d6b40b6a41e3a9af845b604e9acc7aa4f2a0c37009d800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000020554d494120464f554e4445522f4f50455241544f52204c4547414c205041434b0000000000000000000000000000000000000000000000000000000000000042697066733a2f2f626166796265696371676e7a6161347a6d376e6c6b726e757566377864796875676869736e6f756765726a6c696577796e6a746461696f69763565000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000b666f756e6465724e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e656e74657270726973654e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e794e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e79547970650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013636f6d70616e794a7572697364696374696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015636f6d70616e79436f6e7461637444657461696c730000000000000000000000000000000000000000000000000000000000000000000000000000000000000b746f6b656e53796d626f6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009746f6b656e4e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e636f6e7461637444657461696c73000000000000000000000000000000000000" + }); + safeTxs[1] = GnosisTransaction({ + to: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, + value: 0, + data: hex"55f0c0c693ac1365e39b1d8237c84cf969b752ffbb717f7d8144eb47562b4060bcd91c3000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004c00000000000000000000000000000000000000000000000000000000000000052414354494f4e20425920554e414e494d4f5553205752495454454e20434f4e53454e54204f462054484520424f415244204f46204449524543544f5253204f4620554d4941204c41554e434845522053504300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f6261666b7265696372363571626966347670726e74356c326f767a716a62797777676d6e706c7933737a376e656236717a6a6262326e6a63727734000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000b666f756e6465724e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e656e74657270726973654e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e794e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e79547970650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013636f6d70616e794a7572697364696374696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015636f6d70616e79436f6e7461637444657461696c730000000000000000000000000000000000000000000000000000000000000000000000000000000000000b746f6b656e53796d626f6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009746f6b656e4e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e636f6e7461637444657461696c73000000000000000000000000000000000000" + }); + + for (uint256 i = 0; i < safeTxs.length; i++) { + vm.prank(coreDeployment.metalexSafe); + (bool success,) = safeTxs[i].to.call{value: safeTxs[i].value}(safeTxs[i].data); + vm.assertTrue(success); + } + + // Simulate adding a test parent corp officer + // TODO comment out after Umia officer has signed the escrowed signature + parentCoOfficers.push(CompanyOfficer({ + eoa: parentCorpTestOfficer, + name: "Test ParentCo Officer", + contact: "test@parent.com", + title: "Director" + })); + for(uint256 i = 0; true ; i++) { - try parentCoFactory.parentCoOfficers(i) returns (address eoa, string memory name, string memory contact, string memory title) { + try parentCoFactory.parentCoOfficers(i) returns (address eoa, string memory name, string memory contact, string memory title) { parentCoOfficers.push(CompanyOfficer({ eoa: eoa, name: name, @@ -51,6 +86,21 @@ contract DeployParentCoFactoryAcceptanceTest is Test { break; } } + + // Simulate adding the test parent corp officer and have him sign the escrowed signature + // TODO comment out after Umia officer has signed the escrowed signature + + vm.prank(parentCoOfficers[1].eoa); + parentCoFactory.setParentCoOfficers(parentCoOfficers); + + bytes memory escrowSig = _createParentCoSignatureHash( + parentCorpTestOfficerPrivKey, + parentCoOfficers[0].name, + parentCoOfficers[0].contact + ); + + vm.prank(parentCoOfficers[1].eoa); + parentCoFactory.setParentCoSignatureHash(escrowSig); } function test_deploySubCorp() public { From 973ea8f3f78b8515921402932f87b49b86856206 Mon Sep 17 00:00:00 2001 From: detoo Date: Wed, 29 Apr 2026 14:40:55 -0700 Subject: [PATCH 15/20] fix: Safe tx JSON formatter to include chainId --- script/deploy-parentco-factory.s.sol | 2 +- script/libs/SafeUtils.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index cccd70a..43bd792 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -249,7 +249,7 @@ contract DeployParentCoFactoryScript is Script { ) }); - string memory safeTxJson = SafeUtils.formatSafeTxJson(safeTxs); + string memory safeTxJson = SafeUtils.formatSafeTxJson(safeTxs, chainId); console2.log("Safe tx JSON (can be imported to Safe Transaction Builder):"); console2.log("==== JSON data start ===="); diff --git a/script/libs/SafeUtils.sol b/script/libs/SafeUtils.sol index def476b..eb4b3c0 100644 --- a/script/libs/SafeUtils.sol +++ b/script/libs/SafeUtils.sol @@ -35,7 +35,7 @@ library SafeUtils { bytes data; } - function formatSafeTxJson(GnosisTransaction[] memory safeTxs) internal returns (string memory) { + function formatSafeTxJson(GnosisTransaction[] memory safeTxs, uint256 chainId) internal returns (string memory) { SafeTx[] memory convertedSafeTxs = new SafeTx[](safeTxs.length); for (uint256 i = 0; i < safeTxs.length; i++) { convertedSafeTxs[i] = SafeTx({ @@ -50,7 +50,7 @@ library SafeUtils { "SafeTxImport(string version,string chainId,uint256 createdAt,SafeTxMeta meta,SafeTx[] transactions)SafeTxMeta(string name,string description,string txBuilderVersion,string createdFromSafeAddress,string createdFromOwnerAddress,string checksum)SafeTx(address to,string value,bytes data)", abi.encode(SafeTxImport({ version: "1.0", - chainId: "1", + chainId: vm.toString(chainId), createdAt: block.timestamp * 1000, meta: SafeTxMeta({ name: "Transactions Batch", From ed4362ab556874b9db7fbe831fdc79b0bcbd40cf Mon Sep 17 00:00:00 2001 From: 0xPrepop <6125373+merisman@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:35:20 -0400 Subject: [PATCH 16/20] Added Round Manager to corp deployment --- script/deploy-parentco-factory.s.sol | 5 +++-- src/ParentCoFactory.sol | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index 43bd792..515c5ee 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -52,7 +52,7 @@ contract DeployParentCoFactoryScript is Script { parentCoContactDetails: "c/o TTA Corporate Services Limited, Harbour Place, 2nd Floor, North Wing, 103 South Church Street, P.O. Box 472, George Town, Grand Cayman KY1-1106, Cayman Islands", parentCoDefaultDisputeResolution: "confidential, binding arbitration", parentCoOfficers: parentCoOfficers - }); + }); // // testnet // @@ -86,7 +86,8 @@ contract DeployParentCoFactoryScript is Script { // parentCoJurisdiction: "Delaware", // parentCoContactDetails: "test@parentco.example", // parentCoDefaultDisputeResolution: "binding arbitration", -// parentCoOfficers: parentCoOfficers +// parentCoOfficers: parentCoOfficers, +// parentCoSignatureHash: hex"15756f79dff911500a995b7b5af0d43ce5a9e25878831dec6fe63375199c964f0b9be1f4f77788492e0cb29dceca0e62069bd0a8f0c20e3a4d1ed6e88a4b19a81b" // }); } diff --git a/src/ParentCoFactory.sol b/src/ParentCoFactory.sol index d048754..cc2626c 100644 --- a/src/ParentCoFactory.sol +++ b/src/ParentCoFactory.sol @@ -342,8 +342,25 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { dealManagerFactory ); + if (ICyberCorp(cyberCorpAddress).roundManager() != address(0)) { + revert RoundManagerAlreadyExists(); + } + + roundManagerAddress = IRoundManagerFactory(roundManagerFactory).deployRoundManager(salt); + + IRoundManagerInit(roundManagerAddress).initialize( + address(BorgAuthACL(cyberCorpAddress).AUTH()), + cyberCorpAddress, + registryAddress, + ICyberCorpLocal(cyberCorpAddress).issuanceManager(), + roundManagerFactory + ); + + ICyberCorp(cyberCorpAddress).setRoundManager(roundManagerAddress); + BorgAuth(authAddress).updateRole(issuanceManagerAddress, 99); BorgAuth(authAddress).updateRole(dealManagerAddress, 99); + BorgAuth(authAddress).updateRole(roundManagerAddress, 99); emit CorpDeployed(cyberCorpAddress, authAddress, issuanceManagerAddress, dealManagerAddress, roundManagerAddress, address(0), 0, _officer.eoa); } From c16923732ff49b36f1f4a0b277df8fc88b9b5b1b Mon Sep 17 00:00:00 2001 From: detoo Date: Thu, 30 Apr 2026 10:08:51 -0700 Subject: [PATCH 17/20] feat: add missing events --- src/ParentCoFactory.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ParentCoFactory.sol b/src/ParentCoFactory.sol index cc2626c..bb2871f 100644 --- a/src/ParentCoFactory.sol +++ b/src/ParentCoFactory.sol @@ -156,6 +156,8 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { address oldRoundManagerFactory ); + event RoundManagerDeployed(address indexed cyberCorp, address indexed roundManager); + event UriBuilderUpdated(address indexed uriBuilder, address oldUriBuilder); event RegistryAddressUpdated(address indexed registryAddress, address oldRegistryAddress); @@ -358,6 +360,8 @@ contract ParentCoFactory is UUPSUpgradeable, BorgAuthACL, IERC721Receiver { ICyberCorp(cyberCorpAddress).setRoundManager(roundManagerAddress); + emit RoundManagerDeployed(cyberCorpAddress, roundManagerAddress); + BorgAuth(authAddress).updateRole(issuanceManagerAddress, 99); BorgAuth(authAddress).updateRole(dealManagerAddress, 99); BorgAuth(authAddress).updateRole(roundManagerAddress, 99); From 7c933542c015a3185b8f4b3e75f3746d54567cc9 Mon Sep 17 00:00:00 2001 From: detoo Date: Thu, 30 Apr 2026 11:52:48 -0700 Subject: [PATCH 18/20] chore: deploy to prod --- script/deploy-parentco-factory.s.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index 515c5ee..c3889d2 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -73,7 +73,7 @@ contract DeployParentCoFactoryScript is Script { // return runWithArgs({ // chainId: DeploymentConstants.BASE_SEPOLIA, // deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), -// saltStr: "ParentCoFactory.deploy.v2.dev0", +// saltStr: "ParentCoFactory.deploy.v2.dev1", // segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), // segCoDocName: "FOUNDER/OPERATOR LEGAL PACK", // segCoDocUri: "ipfs://parentco-test-segco-template", @@ -81,13 +81,12 @@ contract DeployParentCoFactoryScript is Script { // boardConsentName: "ParentCo Test Board Consent", // boardConsentUri: "ipfs://parentco-test-board-consent-template", // parentCoPayable: 0x8E9603BcB5D974Ed9C870510F3665F67CE5c5bDe, -// parentCoName: "Test ParentCo LLC dev0", +// parentCoName: "Test ParentCo LLC dev1", // parentCoType: "limited liability company", // parentCoJurisdiction: "Delaware", // parentCoContactDetails: "test@parentco.example", // parentCoDefaultDisputeResolution: "binding arbitration", -// parentCoOfficers: parentCoOfficers, -// parentCoSignatureHash: hex"15756f79dff911500a995b7b5af0d43ce5a9e25878831dec6fe63375199c964f0b9be1f4f77788492e0cb29dceca0e62069bd0a8f0c20e3a4d1ed6e88a4b19a81b" +// parentCoOfficers: parentCoOfficers // }); } From 6cf80d956330ca8ca032e46070a285a21e2d8115 Mon Sep 17 00:00:00 2001 From: detoo Date: Thu, 30 Apr 2026 18:14:20 -0700 Subject: [PATCH 19/20] chore: update new prod addresses for tests --- script/libs/DeploymentConstants.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/libs/DeploymentConstants.sol b/script/libs/DeploymentConstants.sol index 8c8d241..b7f6129 100644 --- a/script/libs/DeploymentConstants.sol +++ b/script/libs/DeploymentConstants.sol @@ -86,9 +86,9 @@ library DeploymentConstants { pure returns (UmiaDeployment memory deployment) { - if (chainId == BASE) { + if (chainId == ETH) { return UmiaDeployment({ - parentCoFactory: 0x50512F70A37C90C9AFCe2074AEA5E9F99Dd21Df1, + parentCoFactory: 0x5c6D411600774c8fE1Aa805d78F03202d7FCD47F, segCoTemplateId: 0xd9e0fbb89f8e4e973f05d6b40b6a41e3a9af845b604e9acc7aa4f2a0c37009d8, boardConsentTemplateId: 0x93ac1365e39b1d8237c84cf969b752ffbb717f7d8144eb47562b4060bcd91c30 }); From cff82e9427da5b0c286144839b8fcc68c939092a Mon Sep 17 00:00:00 2001 From: detoo Date: Fri, 1 May 2026 11:53:03 -0700 Subject: [PATCH 20/20] chore: deploy new staging to sepolia --- script/deploy-parentco-factory.s.sol | 2 +- script/libs/DeploymentConstants.sol | 8 ++- test/DeployParentCoFactoryAcceptance.t.sol | 83 +++++++++++----------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/script/deploy-parentco-factory.s.sol b/script/deploy-parentco-factory.s.sol index c3889d2..654627d 100644 --- a/script/deploy-parentco-factory.s.sol +++ b/script/deploy-parentco-factory.s.sol @@ -71,7 +71,7 @@ contract DeployParentCoFactoryScript is Script { // }); // // return runWithArgs({ -// chainId: DeploymentConstants.BASE_SEPOLIA, +// chainId: DeploymentConstants.ETH_SEPOLIA, // deployerPrivateKey: vm.envUint("PRIVATE_KEY_MAIN"), // saltStr: "ParentCoFactory.deploy.v2.dev1", // segCoTemplateId: keccak256("ParentCo.Test2.SegCo.v1"), diff --git a/script/libs/DeploymentConstants.sol b/script/libs/DeploymentConstants.sol index b7f6129..8b23ff4 100644 --- a/script/libs/DeploymentConstants.sol +++ b/script/libs/DeploymentConstants.sol @@ -92,9 +92,15 @@ library DeploymentConstants { segCoTemplateId: 0xd9e0fbb89f8e4e973f05d6b40b6a41e3a9af845b604e9acc7aa4f2a0c37009d8, boardConsentTemplateId: 0x93ac1365e39b1d8237c84cf969b752ffbb717f7d8144eb47562b4060bcd91c30 }); + } else if (chainId == ETH_SEPOLIA) { + return UmiaDeployment({ + parentCoFactory: 0x0c6Fc81BEd7f91f7a3b3594CCc66484893634Bf9, + segCoTemplateId: 0xb6da5c8e53767592c0eeb4c5c0d77eae7e1e2e795190e7237d837b3fbc98ed75, + boardConsentTemplateId: 0xc02175e98621a996529fb751b30e0b7a8344ece3b00f46a29c1e904c9da87a46 + }); } else if (chainId == BASE_SEPOLIA) { return UmiaDeployment({ - parentCoFactory: 0x478ee34c618E9339Ae2DD8100Df7ec535eb24D29, + parentCoFactory: 0xC1304898FAfF45cA2B07C0f4E10B77843eD5a47B, segCoTemplateId: 0xb6da5c8e53767592c0eeb4c5c0d77eae7e1e2e795190e7237d837b3fbc98ed75, boardConsentTemplateId: 0xc02175e98621a996529fb751b30e0b7a8344ece3b00f46a29c1e904c9da87a46 }); diff --git a/test/DeployParentCoFactoryAcceptance.t.sol b/test/DeployParentCoFactoryAcceptance.t.sol index dfa4654..e2c2566 100644 --- a/test/DeployParentCoFactoryAcceptance.t.sol +++ b/test/DeployParentCoFactoryAcceptance.t.sol @@ -44,35 +44,33 @@ contract DeployParentCoFactoryAcceptanceTest is Test { parentCoFactory = ParentCoFactory(umiaDeployment.parentCoFactory); - // Simulate MetaLeX creating the templates - // TODO comment out after MetaLeX safe executed the safe txs - - GnosisTransaction[] memory safeTxs = new GnosisTransaction[](2); - safeTxs[0] = GnosisTransaction({ - to: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, - value: 0, - data: hex"55f0c0c6d9e0fbb89f8e4e973f05d6b40b6a41e3a9af845b604e9acc7aa4f2a0c37009d800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000020554d494120464f554e4445522f4f50455241544f52204c4547414c205041434b0000000000000000000000000000000000000000000000000000000000000042697066733a2f2f626166796265696371676e7a6161347a6d376e6c6b726e757566377864796875676869736e6f756765726a6c696577796e6a746461696f69763565000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000b666f756e6465724e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e656e74657270726973654e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e794e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e79547970650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013636f6d70616e794a7572697364696374696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015636f6d70616e79436f6e7461637444657461696c730000000000000000000000000000000000000000000000000000000000000000000000000000000000000b746f6b656e53796d626f6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009746f6b656e4e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e636f6e7461637444657461696c73000000000000000000000000000000000000" - }); - safeTxs[1] = GnosisTransaction({ - to: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, - value: 0, - data: hex"55f0c0c693ac1365e39b1d8237c84cf969b752ffbb717f7d8144eb47562b4060bcd91c3000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004c00000000000000000000000000000000000000000000000000000000000000052414354494f4e20425920554e414e494d4f5553205752495454454e20434f4e53454e54204f462054484520424f415244204f46204449524543544f5253204f4620554d4941204c41554e434845522053504300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f6261666b7265696372363571626966347670726e74356c326f767a716a62797777676d6e706c7933737a376e656236717a6a6262326e6a63727734000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000b666f756e6465724e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e656e74657270726973654e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e794e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e79547970650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013636f6d70616e794a7572697364696374696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015636f6d70616e79436f6e7461637444657461696c730000000000000000000000000000000000000000000000000000000000000000000000000000000000000b746f6b656e53796d626f6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009746f6b656e4e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e636f6e7461637444657461696c73000000000000000000000000000000000000" - }); - - for (uint256 i = 0; i < safeTxs.length; i++) { - vm.prank(coreDeployment.metalexSafe); - (bool success,) = safeTxs[i].to.call{value: safeTxs[i].value}(safeTxs[i].data); - vm.assertTrue(success); - } - - // Simulate adding a test parent corp officer - // TODO comment out after Umia officer has signed the escrowed signature - parentCoOfficers.push(CompanyOfficer({ - eoa: parentCorpTestOfficer, - name: "Test ParentCo Officer", - contact: "test@parent.com", - title: "Director" - })); +// // Simulate MetaLeX creating the templates +// +// GnosisTransaction[] memory safeTxs = new GnosisTransaction[](2); +// safeTxs[0] = GnosisTransaction({ +// to: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, +// value: 0, +// data: hex"55f0c0c6d9e0fbb89f8e4e973f05d6b40b6a41e3a9af845b604e9acc7aa4f2a0c37009d800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000020554d494120464f554e4445522f4f50455241544f52204c4547414c205041434b0000000000000000000000000000000000000000000000000000000000000042697066733a2f2f626166796265696371676e7a6161347a6d376e6c6b726e757566377864796875676869736e6f756765726a6c696577796e6a746461696f69763565000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000b666f756e6465724e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e656e74657270726973654e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e794e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e79547970650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013636f6d70616e794a7572697364696374696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015636f6d70616e79436f6e7461637444657461696c730000000000000000000000000000000000000000000000000000000000000000000000000000000000000b746f6b656e53796d626f6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009746f6b656e4e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e636f6e7461637444657461696c73000000000000000000000000000000000000" +// }); +// safeTxs[1] = GnosisTransaction({ +// to: 0xa9E808B8eCBB60Bb19abF026B5b863215BC4c134, +// value: 0, +// data: hex"55f0c0c693ac1365e39b1d8237c84cf969b752ffbb717f7d8144eb47562b4060bcd91c3000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000004c00000000000000000000000000000000000000000000000000000000000000052414354494f4e20425920554e414e494d4f5553205752495454454e20434f4e53454e54204f462054484520424f415244204f46204449524543544f5253204f4620554d4941204c41554e434845522053504300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042697066733a2f2f6261666b7265696372363571626966347670726e74356c326f767a716a62797777676d6e706c7933737a376e656236717a6a6262326e6a63727734000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000b666f756e6465724e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e656e74657270726973654e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e794e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b636f6d70616e79547970650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013636f6d70616e794a7572697364696374696f6e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015636f6d70616e79436f6e7461637444657461696c730000000000000000000000000000000000000000000000000000000000000000000000000000000000000b746f6b656e53796d626f6c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009746f6b656e4e616d65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e636f6e7461637444657461696c73000000000000000000000000000000000000" +// }); +// +// for (uint256 i = 0; i < safeTxs.length; i++) { +// vm.prank(coreDeployment.metalexSafe); +// (bool success,) = safeTxs[i].to.call{value: safeTxs[i].value}(safeTxs[i].data); +// vm.assertTrue(success); +// } + +// // Simulate adding a test parent corp officer +// parentCoOfficers.push(CompanyOfficer({ +// eoa: parentCorpTestOfficer, +// name: "Test ParentCo Officer", +// contact: "test@parent.com", +// title: "Director" +// })); for(uint256 i = 0; true ; i++) { try parentCoFactory.parentCoOfficers(i) returns (address eoa, string memory name, string memory contact, string memory title) { @@ -87,20 +85,19 @@ contract DeployParentCoFactoryAcceptanceTest is Test { } } - // Simulate adding the test parent corp officer and have him sign the escrowed signature - // TODO comment out after Umia officer has signed the escrowed signature - - vm.prank(parentCoOfficers[1].eoa); - parentCoFactory.setParentCoOfficers(parentCoOfficers); - - bytes memory escrowSig = _createParentCoSignatureHash( - parentCorpTestOfficerPrivKey, - parentCoOfficers[0].name, - parentCoOfficers[0].contact - ); - - vm.prank(parentCoOfficers[1].eoa); - parentCoFactory.setParentCoSignatureHash(escrowSig); +// // Simulate adding the test parent corp officer and have him sign the escrowed signature +// +// vm.prank(parentCoOfficers[1].eoa); +// parentCoFactory.setParentCoOfficers(parentCoOfficers); +// +// bytes memory escrowSig = _createParentCoSignatureHash( +// parentCorpTestOfficerPrivKey, +// parentCoOfficers[0].name, +// parentCoOfficers[0].contact +// ); +// +// vm.prank(parentCoOfficers[1].eoa); +// parentCoFactory.setParentCoSignatureHash(escrowSig); } function test_deploySubCorp() public {