Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 167 additions & 1 deletion script/DeployOrg.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.20;
import "forge-std/Script.sol";
import "forge-std/console.sol";

import {OrgDeployer} from "../src/OrgDeployer.sol";
import {OrgDeployer, ITaskManagerBootstrap} from "../src/OrgDeployer.sol";
import {IHybridVotingInit} from "../src/libs/ModuleDeploymentLib.sol";
import {RoleConfigStructs} from "../src/libs/RoleConfigStructs.sol";
import {ModulesFactory} from "../src/factories/ModulesFactory.sol";
Expand Down Expand Up @@ -40,6 +40,7 @@ contract DeployOrg is Script {
address[] ddInitialTargets;
bool withPaymaster;
bool withEducationHub; // Whether to deploy EducationHub (default: true)
BootstrapConfigJson bootstrap; // Optional: initial projects and tasks
}

struct QuorumConfig {
Expand Down Expand Up @@ -106,6 +107,33 @@ contract DeployOrg is Script {
uint256[] ddCreatorRoles;
}

// Bootstrap config structs for initial project/task creation
struct BootstrapProjectConfigJson {
string title;
bytes32 metadataHash;
uint256 cap;
address[] managers;
uint256[] createRoles;
uint256[] claimRoles;
uint256[] reviewRoles;
uint256[] assignRoles;
}

struct BootstrapTaskConfigJson {
uint8 projectIndex;
uint256 payout;
string title;
bytes32 metadataHash;
address bountyToken;
uint256 bountyPayout;
bool requiresApplication;
}

struct BootstrapConfigJson {
BootstrapProjectConfigJson[] projects;
BootstrapTaskConfigJson[] tasks;
}

/*═══════════════════════════ MAIN DEPLOYMENT ═══════════════════════════*/

function run() public {
Expand Down Expand Up @@ -325,9 +353,101 @@ contract DeployOrg is Script {
bytes memory ddTargetsData = vm.parseJson(configJson, ".ddInitialTargets");
config.ddInitialTargets = abi.decode(ddTargetsData, (address[]));

// Parse bootstrap config (optional)
config.bootstrap = _parseBootstrapConfig(configJson);

return config;
}

function _parseBootstrapConfig(string memory configJson) internal returns (BootstrapConfigJson memory bootstrap) {
// Count bootstrap projects
uint256 projectsLength = 0;
for (uint256 i = 0; i < 100; i++) {
try vm.parseJsonString(configJson, string.concat(".bootstrap.projects[", vm.toString(i), "].title")) returns (
string memory
) {
projectsLength++;
} catch {
break;
}
}

if (projectsLength == 0) {
return bootstrap; // No bootstrap config
}

bootstrap.projects = new BootstrapProjectConfigJson[](projectsLength);
for (uint256 i = 0; i < projectsLength; i++) {
string memory basePath = string.concat(".bootstrap.projects[", vm.toString(i), "]");

bootstrap.projects[i].title = vm.parseJsonString(configJson, string.concat(basePath, ".title"));

// Parse metadataHash (optional, default to 0)
try vm.parseJsonBytes32(configJson, string.concat(basePath, ".metadataHash")) returns (bytes32 hash) {
bootstrap.projects[i].metadataHash = hash;
} catch {
bootstrap.projects[i].metadataHash = bytes32(0);
}

bootstrap.projects[i].cap = vm.parseJsonUint(configJson, string.concat(basePath, ".cap"));

// Parse managers array (optional)
try vm.parseJson(configJson, string.concat(basePath, ".managers")) returns (bytes memory managersData) {
bootstrap.projects[i].managers = abi.decode(managersData, (address[]));
} catch {
bootstrap.projects[i].managers = new address[](0);
}

// Parse role arrays
bytes memory createRolesData = vm.parseJson(configJson, string.concat(basePath, ".createRoles"));
bootstrap.projects[i].createRoles = abi.decode(createRolesData, (uint256[]));

bytes memory claimRolesData = vm.parseJson(configJson, string.concat(basePath, ".claimRoles"));
bootstrap.projects[i].claimRoles = abi.decode(claimRolesData, (uint256[]));

bytes memory reviewRolesData = vm.parseJson(configJson, string.concat(basePath, ".reviewRoles"));
bootstrap.projects[i].reviewRoles = abi.decode(reviewRolesData, (uint256[]));

bytes memory assignRolesData = vm.parseJson(configJson, string.concat(basePath, ".assignRoles"));
bootstrap.projects[i].assignRoles = abi.decode(assignRolesData, (uint256[]));
}

// Count bootstrap tasks
uint256 tasksLength = 0;
for (uint256 i = 0; i < 100; i++) {
try vm.parseJsonString(configJson, string.concat(".bootstrap.tasks[", vm.toString(i), "].title")) returns (
string memory
) {
tasksLength++;
} catch {
break;
}
}

bootstrap.tasks = new BootstrapTaskConfigJson[](tasksLength);
for (uint256 i = 0; i < tasksLength; i++) {
string memory basePath = string.concat(".bootstrap.tasks[", vm.toString(i), "]");

bootstrap.tasks[i].projectIndex = uint8(vm.parseJsonUint(configJson, string.concat(basePath, ".projectIndex")));
bootstrap.tasks[i].payout = vm.parseJsonUint(configJson, string.concat(basePath, ".payout"));
bootstrap.tasks[i].title = vm.parseJsonString(configJson, string.concat(basePath, ".title"));

// Parse metadataHash (optional, default to 0)
try vm.parseJsonBytes32(configJson, string.concat(basePath, ".metadataHash")) returns (bytes32 hash) {
bootstrap.tasks[i].metadataHash = hash;
} catch {
bootstrap.tasks[i].metadataHash = bytes32(0);
}

bootstrap.tasks[i].bountyToken = vm.parseJsonAddress(configJson, string.concat(basePath, ".bountyToken"));
bootstrap.tasks[i].bountyPayout = vm.parseJsonUint(configJson, string.concat(basePath, ".bountyPayout"));
bootstrap.tasks[i].requiresApplication =
vm.parseJsonBool(configJson, string.concat(basePath, ".requiresApplication"));
}

return bootstrap;
}

/*═══════════════════════════ PARAM BUILDING ═══════════════════════════*/

/**
Expand Down Expand Up @@ -433,9 +553,55 @@ contract DeployOrg is Script {
// Build education hub config
params.educationHubConfig = ModulesFactory.EducationHubConfig({enabled: config.withEducationHub});

// Build bootstrap config for initial projects/tasks
params.bootstrap = _buildBootstrapConfig(config.bootstrap);

return params;
}

function _buildBootstrapConfig(BootstrapConfigJson memory bootstrapJson)
internal
pure
returns (OrgDeployer.BootstrapConfig memory bootstrap)
{
if (bootstrapJson.projects.length == 0) {
return bootstrap; // Empty bootstrap
}

// Build project configs
// Note: Role indices will be converted to hat IDs by OrgDeployer at deployment time
bootstrap.projects = new ITaskManagerBootstrap.BootstrapProjectConfig[](bootstrapJson.projects.length);
for (uint256 i = 0; i < bootstrapJson.projects.length; i++) {
bootstrap.projects[i] = ITaskManagerBootstrap.BootstrapProjectConfig({
title: bytes(bootstrapJson.projects[i].title),
metadataHash: bootstrapJson.projects[i].metadataHash,
cap: bootstrapJson.projects[i].cap,
managers: bootstrapJson.projects[i].managers,
// Note: These are role indices, OrgDeployer will resolve to hat IDs
createHats: bootstrapJson.projects[i].createRoles,
claimHats: bootstrapJson.projects[i].claimRoles,
reviewHats: bootstrapJson.projects[i].reviewRoles,
assignHats: bootstrapJson.projects[i].assignRoles
});
}

// Build task configs
bootstrap.tasks = new ITaskManagerBootstrap.BootstrapTaskConfig[](bootstrapJson.tasks.length);
for (uint256 i = 0; i < bootstrapJson.tasks.length; i++) {
bootstrap.tasks[i] = ITaskManagerBootstrap.BootstrapTaskConfig({
projectIndex: bootstrapJson.tasks[i].projectIndex,
payout: bootstrapJson.tasks[i].payout,
title: bytes(bootstrapJson.tasks[i].title),
metadataHash: bootstrapJson.tasks[i].metadataHash,
bountyToken: bootstrapJson.tasks[i].bountyToken,
bountyPayout: bootstrapJson.tasks[i].bountyPayout,
requiresApplication: bootstrapJson.tasks[i].requiresApplication
});
}

return bootstrap;
}

/*═══════════════════════════ OUTPUT ═══════════════════════════*/

function _outputDeployment(OrgConfigJson memory config, OrgDeployer.DeploymentResult memory result) internal view {
Expand Down
Loading
Loading