Skip to content

Commit

Permalink
Create SchemeFactory (#717)
Browse files Browse the repository at this point in the history
* Create SchemeFactory

* Allow update scheme

* Bump version

* Lint

* Allow unregister scheme only

* Arc -> ArcHive

* Add return doc

* remove redundant comment
  • Loading branch information
ben-kaufman authored Mar 12, 2020
1 parent c983304 commit 9078a97
Show file tree
Hide file tree
Showing 6 changed files with 370 additions and 22 deletions.
172 changes: 172 additions & 0 deletions contracts/schemes/SchemeFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
pragma solidity ^0.5.16;

import "@daostack/infra-experimental/contracts/votingMachines/IntVoteInterface.sol";
import "@daostack/infra-experimental/contracts/votingMachines/VotingMachineCallbacksInterface.sol";
import "../votingMachines/VotingMachineCallbacks.sol";
import "../utils/DAOFactory.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";


/**
* @title A factory and registrar for Schemes for organizations
* @dev The SchemeFactory is used for deploying and registering schemes to organisations
*/
contract SchemeFactory is Initializable, VotingMachineCallbacks, ProposalExecuteInterface {
event NewSchemeProposal(
address indexed _avatar,
bytes32 indexed _proposalId,
address indexed _intVoteInterface,
string _schemeName,
bytes _schemeData,
uint64[3] _packageVersion,
bytes4 _permissions,
address _schemeToReplace,
string _descriptionHash
);

event ProposalExecuted(address indexed _avatar, bytes32 indexed _proposalId, int256 _param);
event ProposalDeleted(address indexed _avatar, bytes32 indexed _proposalId);

// a proposal to add or remove a scheme to/from the an organization
struct Proposal {
string schemeName;
bytes schemeData;
uint64[3] packageVersion;
address schemeToReplace;
bytes4 permissions;
}

mapping(bytes32=>Proposal) public proposals;

IntVoteInterface public votingMachine;
bytes32 public voteParams;
Avatar public avatar;
DAOFactory public daoFactory;

/**
* @dev initialize
* @param _avatar the avatar this scheme referring to.
* @param _votingMachine the voting machines address to
* @param _voteParams voting machine parameters to register scheme.
*/
function initialize(
Avatar _avatar,
IntVoteInterface _votingMachine,
bytes32 _voteParams,
DAOFactory _daoFactory
)
external
initializer
{
require(_avatar != Avatar(0), "avatar cannot be zero");
avatar = _avatar;
votingMachine = _votingMachine;
voteParams = _voteParams;
daoFactory = _daoFactory;
}

/**
* @dev execution of proposals, can only be called by the voting machine in which the vote is held.
* @param _proposalId the ID of the voting in the voting machine
* @param _decision the voting result, 1 yes and 2 is no.
* @return true (if function did not revert)
*/
function executeProposal(bytes32 _proposalId, int256 _decision)
external
onlyVotingMachine(_proposalId)
returns(bool) {
Proposal memory proposal = proposals[_proposalId];
delete proposals[_proposalId];
emit ProposalDeleted(address(avatar), _proposalId);
Controller controller = Controller(avatar.owner());
if (_decision == 1) {
if (bytes(proposal.schemeName).length > 0) {
address scheme = address(daoFactory.createInstance(
proposal.packageVersion,
proposal.schemeName,
address(avatar),
proposal.schemeData));

require(controller.registerScheme(scheme, proposal.permissions), "faild to register new scheme");
}


if (proposal.schemeToReplace != address(0) && controller.isSchemeRegistered(proposal.schemeToReplace)) {
require(controller.unregisterScheme(proposal.schemeToReplace), "faild to unregister old scheme");
}
}
emit ProposalExecuted(address(avatar), _proposalId, _decision);
return true;
}

/**
* @dev create a proposal to register a scheme
* @param _packageVersion the Arc version to use for deploying the scheme
* @param _schemeName the name of the scheme to be added
* @param _schemeData initialize data for the scheme to be added
* @param _permissions the permission of the scheme to be registered
* @param _schemeToReplace address of scheme to replace with the new scheme (zero for none)
* @param _descriptionHash proposal's description hash
* @return a proposal Id
*/
function proposeScheme(
uint64[3] memory _packageVersion,
string memory _schemeName,
bytes memory _schemeData,
bytes4 _permissions,
address _schemeToReplace,
string memory _descriptionHash
)
public
returns(bytes32)
{
if (bytes(_schemeName).length > 0) {
require(
daoFactory.getImplementation(_packageVersion, _schemeName) != address(0),
"scheme name does not exist in ArcHive"
);
} else if (_schemeToReplace != address(0)) {
Controller controller = Controller(avatar.owner());
require(
controller.isSchemeRegistered(_schemeToReplace),
"scheme to replace is not registered in the organization"
);
} else {
revert("proposal must have a scheme name to reister or address to unregister");
}

bytes32 proposalId = votingMachine.propose(
2,
voteParams,
msg.sender,
address(avatar)
);

Proposal memory proposal = Proposal({
schemeName: _schemeName,
schemeData: _schemeData,
packageVersion: _packageVersion,
schemeToReplace: _schemeToReplace,
permissions: _permissions
});

emit NewSchemeProposal(
address(avatar),
proposalId,
address(votingMachine),
_schemeName,
_schemeData,
_packageVersion,
_permissions,
_schemeToReplace,
_descriptionHash
);

proposals[proposalId] = proposal;
proposalsInfo[address(votingMachine)][proposalId] = ProposalInfo({
blockNumber:block.number,
avatar:avatar
});
return proposalId;
}
}
47 changes: 30 additions & 17 deletions contracts/utils/DAOFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract DAOFactory is Initializable {
address indexed _reputation,
address _daotoken
);

event InitialSchemesSet (address indexed _avatar);
event SchemeInstance(address indexed _scheme, string _name);
/**
Expand Down Expand Up @@ -154,16 +154,41 @@ contract DAOFactory is Initializable {
public
payable
returns (AdminUpgradeabilityProxy) {
Package package;
uint64[3] memory version = getPackageVersion(_packageVersion);
(package, ) = app.getPackage(PACKAGE_NAME);
ImplementationProvider provider = ImplementationProvider(package.getContract(version));
address implementation = provider.getImplementation(_contractName);
address implementation = getImplementation(version, _contractName);
AdminUpgradeabilityProxy proxy = (new AdminUpgradeabilityProxy).value(msg.value)(implementation, _admin, _data);
emit ProxyCreated(address(proxy), implementation, _contractName, version);
return proxy;
}

/**
* @dev Helper function to get the implementation contract by a contract name.
* @param _version of the instance.
* @param _contractName Name of the contract.
* @return Address of the new implementation contract.
*/
function getImplementation(uint64[3] memory _version, string memory _contractName)
public
view
returns (address) {
Package package;
(package, ) = app.getPackage(PACKAGE_NAME);
ImplementationProvider provider = ImplementationProvider(package.getContract(_version));
return provider.getImplementation(_contractName);
}

function getPackageVersion(uint64[3] memory _version) public view returns(uint64[3] memory version) {
Package package;
uint64[3] memory latestVersion;
(package, latestVersion) = app.getPackage(PACKAGE_NAME);
if (package.getContract(_version) == address(0)) {
require(package.getContract(latestVersion) != address(0), "ImplementationProvider does not exist");
version = latestVersion;
} else {
version = _version;
}
}

/**
* @dev Set initial schemes for the organization.
* @param _avatar organization avatar (returns from forgeOrg)
Expand Down Expand Up @@ -293,16 +318,4 @@ contract DAOFactory is Initializable {
return (address(avatar));
}

function getPackageVersion(uint64[3] memory _version) private view returns(uint64[3] memory version) {
Package package;
uint64[3] memory latestVersion;
(package, latestVersion) = app.getPackage(PACKAGE_NAME);
if (package.getContract(_version) == address(0)) {
require(package.getContract(latestVersion) != address(0), "ImplementationProvider does not exist");
version = latestVersion;
} else {
version = _version;
}
}

}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@daostack/arc-experimental",
"version": "0.1.1-rc.4",
"version": "0.1.1-rc.5",
"description": "A platform for building DAOs",
"files": [
"contracts/",
Expand Down
3 changes: 3 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const ContributionReward = artifacts.require("./ContributionReward.sol");
const Competition = artifacts.require("./Competition.sol");
const ContributionRewardExt = artifacts.require("./ContributionRewardExt.sol");
const SchemeRegistrar = artifacts.require("./SchemeRegistrar.sol");
const SchemeFactory = artifacts.require("./SchemeFactory.sol");
const GenericScheme = artifacts.require("./GenericScheme.sol");
const UpgradeScheme = artifacts.require("./UpgradeScheme.sol");
const Auction4Reputation = artifacts.require("./Auction4Reputation.sol");
Expand Down Expand Up @@ -165,6 +166,7 @@ export const registrationAddVersionToPackege = async function (registration,vers
registration.competition = await Competition.new();
registration.contributionRewardExt = await ContributionRewardExt.new();
registration.schemeRegistrar = await SchemeRegistrar.new();
registration.schemeFactory = await SchemeFactory.new();
registration.genericScheme = await GenericScheme.new();
registration.upgradeScheme = await UpgradeScheme.new();
registration.auction4Reputation = await Auction4Reputation.new();
Expand All @@ -190,6 +192,7 @@ export const registrationAddVersionToPackege = async function (registration,vers
await implementationDirectory.setImplementation("Competition",registration.competition.address);
await implementationDirectory.setImplementation("ContributionRewardExt",registration.contributionRewardExt.address);
await implementationDirectory.setImplementation("SchemeRegistrar",registration.schemeRegistrar.address);
await implementationDirectory.setImplementation("SchemeFactory",registration.schemeFactory.address);
await implementationDirectory.setImplementation("GenericScheme",registration.genericScheme.address);
await implementationDirectory.setImplementation("UpgradeScheme",registration.upgradeScheme.address);
await implementationDirectory.setImplementation("Auction4Reputation",registration.auction4Reputation.address);
Expand Down
Loading

0 comments on commit 9078a97

Please sign in to comment.