Skip to content

Commit 9078a97

Browse files
authored
Create SchemeFactory (#717)
* Create SchemeFactory * Allow update scheme * Bump version * Lint * Allow unregister scheme only * Arc -> ArcHive * Add return doc * remove redundant comment
1 parent c983304 commit 9078a97

File tree

6 files changed

+370
-22
lines changed

6 files changed

+370
-22
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
pragma solidity ^0.5.16;
2+
3+
import "@daostack/infra-experimental/contracts/votingMachines/IntVoteInterface.sol";
4+
import "@daostack/infra-experimental/contracts/votingMachines/VotingMachineCallbacksInterface.sol";
5+
import "../votingMachines/VotingMachineCallbacks.sol";
6+
import "../utils/DAOFactory.sol";
7+
import "@openzeppelin/upgrades/contracts/Initializable.sol";
8+
9+
10+
/**
11+
* @title A factory and registrar for Schemes for organizations
12+
* @dev The SchemeFactory is used for deploying and registering schemes to organisations
13+
*/
14+
contract SchemeFactory is Initializable, VotingMachineCallbacks, ProposalExecuteInterface {
15+
event NewSchemeProposal(
16+
address indexed _avatar,
17+
bytes32 indexed _proposalId,
18+
address indexed _intVoteInterface,
19+
string _schemeName,
20+
bytes _schemeData,
21+
uint64[3] _packageVersion,
22+
bytes4 _permissions,
23+
address _schemeToReplace,
24+
string _descriptionHash
25+
);
26+
27+
event ProposalExecuted(address indexed _avatar, bytes32 indexed _proposalId, int256 _param);
28+
event ProposalDeleted(address indexed _avatar, bytes32 indexed _proposalId);
29+
30+
// a proposal to add or remove a scheme to/from the an organization
31+
struct Proposal {
32+
string schemeName;
33+
bytes schemeData;
34+
uint64[3] packageVersion;
35+
address schemeToReplace;
36+
bytes4 permissions;
37+
}
38+
39+
mapping(bytes32=>Proposal) public proposals;
40+
41+
IntVoteInterface public votingMachine;
42+
bytes32 public voteParams;
43+
Avatar public avatar;
44+
DAOFactory public daoFactory;
45+
46+
/**
47+
* @dev initialize
48+
* @param _avatar the avatar this scheme referring to.
49+
* @param _votingMachine the voting machines address to
50+
* @param _voteParams voting machine parameters to register scheme.
51+
*/
52+
function initialize(
53+
Avatar _avatar,
54+
IntVoteInterface _votingMachine,
55+
bytes32 _voteParams,
56+
DAOFactory _daoFactory
57+
)
58+
external
59+
initializer
60+
{
61+
require(_avatar != Avatar(0), "avatar cannot be zero");
62+
avatar = _avatar;
63+
votingMachine = _votingMachine;
64+
voteParams = _voteParams;
65+
daoFactory = _daoFactory;
66+
}
67+
68+
/**
69+
* @dev execution of proposals, can only be called by the voting machine in which the vote is held.
70+
* @param _proposalId the ID of the voting in the voting machine
71+
* @param _decision the voting result, 1 yes and 2 is no.
72+
* @return true (if function did not revert)
73+
*/
74+
function executeProposal(bytes32 _proposalId, int256 _decision)
75+
external
76+
onlyVotingMachine(_proposalId)
77+
returns(bool) {
78+
Proposal memory proposal = proposals[_proposalId];
79+
delete proposals[_proposalId];
80+
emit ProposalDeleted(address(avatar), _proposalId);
81+
Controller controller = Controller(avatar.owner());
82+
if (_decision == 1) {
83+
if (bytes(proposal.schemeName).length > 0) {
84+
address scheme = address(daoFactory.createInstance(
85+
proposal.packageVersion,
86+
proposal.schemeName,
87+
address(avatar),
88+
proposal.schemeData));
89+
90+
require(controller.registerScheme(scheme, proposal.permissions), "faild to register new scheme");
91+
}
92+
93+
94+
if (proposal.schemeToReplace != address(0) && controller.isSchemeRegistered(proposal.schemeToReplace)) {
95+
require(controller.unregisterScheme(proposal.schemeToReplace), "faild to unregister old scheme");
96+
}
97+
}
98+
emit ProposalExecuted(address(avatar), _proposalId, _decision);
99+
return true;
100+
}
101+
102+
/**
103+
* @dev create a proposal to register a scheme
104+
* @param _packageVersion the Arc version to use for deploying the scheme
105+
* @param _schemeName the name of the scheme to be added
106+
* @param _schemeData initialize data for the scheme to be added
107+
* @param _permissions the permission of the scheme to be registered
108+
* @param _schemeToReplace address of scheme to replace with the new scheme (zero for none)
109+
* @param _descriptionHash proposal's description hash
110+
* @return a proposal Id
111+
*/
112+
function proposeScheme(
113+
uint64[3] memory _packageVersion,
114+
string memory _schemeName,
115+
bytes memory _schemeData,
116+
bytes4 _permissions,
117+
address _schemeToReplace,
118+
string memory _descriptionHash
119+
)
120+
public
121+
returns(bytes32)
122+
{
123+
if (bytes(_schemeName).length > 0) {
124+
require(
125+
daoFactory.getImplementation(_packageVersion, _schemeName) != address(0),
126+
"scheme name does not exist in ArcHive"
127+
);
128+
} else if (_schemeToReplace != address(0)) {
129+
Controller controller = Controller(avatar.owner());
130+
require(
131+
controller.isSchemeRegistered(_schemeToReplace),
132+
"scheme to replace is not registered in the organization"
133+
);
134+
} else {
135+
revert("proposal must have a scheme name to reister or address to unregister");
136+
}
137+
138+
bytes32 proposalId = votingMachine.propose(
139+
2,
140+
voteParams,
141+
msg.sender,
142+
address(avatar)
143+
);
144+
145+
Proposal memory proposal = Proposal({
146+
schemeName: _schemeName,
147+
schemeData: _schemeData,
148+
packageVersion: _packageVersion,
149+
schemeToReplace: _schemeToReplace,
150+
permissions: _permissions
151+
});
152+
153+
emit NewSchemeProposal(
154+
address(avatar),
155+
proposalId,
156+
address(votingMachine),
157+
_schemeName,
158+
_schemeData,
159+
_packageVersion,
160+
_permissions,
161+
_schemeToReplace,
162+
_descriptionHash
163+
);
164+
165+
proposals[proposalId] = proposal;
166+
proposalsInfo[address(votingMachine)][proposalId] = ProposalInfo({
167+
blockNumber:block.number,
168+
avatar:avatar
169+
});
170+
return proposalId;
171+
}
172+
}

contracts/utils/DAOFactory.sol

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ contract DAOFactory is Initializable {
2020
address indexed _reputation,
2121
address _daotoken
2222
);
23-
23+
2424
event InitialSchemesSet (address indexed _avatar);
2525
event SchemeInstance(address indexed _scheme, string _name);
2626
/**
@@ -154,16 +154,41 @@ contract DAOFactory is Initializable {
154154
public
155155
payable
156156
returns (AdminUpgradeabilityProxy) {
157-
Package package;
158157
uint64[3] memory version = getPackageVersion(_packageVersion);
159-
(package, ) = app.getPackage(PACKAGE_NAME);
160-
ImplementationProvider provider = ImplementationProvider(package.getContract(version));
161-
address implementation = provider.getImplementation(_contractName);
158+
address implementation = getImplementation(version, _contractName);
162159
AdminUpgradeabilityProxy proxy = (new AdminUpgradeabilityProxy).value(msg.value)(implementation, _admin, _data);
163160
emit ProxyCreated(address(proxy), implementation, _contractName, version);
164161
return proxy;
165162
}
166163

164+
/**
165+
* @dev Helper function to get the implementation contract by a contract name.
166+
* @param _version of the instance.
167+
* @param _contractName Name of the contract.
168+
* @return Address of the new implementation contract.
169+
*/
170+
function getImplementation(uint64[3] memory _version, string memory _contractName)
171+
public
172+
view
173+
returns (address) {
174+
Package package;
175+
(package, ) = app.getPackage(PACKAGE_NAME);
176+
ImplementationProvider provider = ImplementationProvider(package.getContract(_version));
177+
return provider.getImplementation(_contractName);
178+
}
179+
180+
function getPackageVersion(uint64[3] memory _version) public view returns(uint64[3] memory version) {
181+
Package package;
182+
uint64[3] memory latestVersion;
183+
(package, latestVersion) = app.getPackage(PACKAGE_NAME);
184+
if (package.getContract(_version) == address(0)) {
185+
require(package.getContract(latestVersion) != address(0), "ImplementationProvider does not exist");
186+
version = latestVersion;
187+
} else {
188+
version = _version;
189+
}
190+
}
191+
167192
/**
168193
* @dev Set initial schemes for the organization.
169194
* @param _avatar organization avatar (returns from forgeOrg)
@@ -293,16 +318,4 @@ contract DAOFactory is Initializable {
293318
return (address(avatar));
294319
}
295320

296-
function getPackageVersion(uint64[3] memory _version) private view returns(uint64[3] memory version) {
297-
Package package;
298-
uint64[3] memory latestVersion;
299-
(package, latestVersion) = app.getPackage(PACKAGE_NAME);
300-
if (package.getContract(_version) == address(0)) {
301-
require(package.getContract(latestVersion) != address(0), "ImplementationProvider does not exist");
302-
version = latestVersion;
303-
} else {
304-
version = _version;
305-
}
306-
}
307-
308321
}

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@daostack/arc-experimental",
3-
"version": "0.1.1-rc.4",
3+
"version": "0.1.1-rc.5",
44
"description": "A platform for building DAOs",
55
"files": [
66
"contracts/",

test/helpers.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const ContributionReward = artifacts.require("./ContributionReward.sol");
1919
const Competition = artifacts.require("./Competition.sol");
2020
const ContributionRewardExt = artifacts.require("./ContributionRewardExt.sol");
2121
const SchemeRegistrar = artifacts.require("./SchemeRegistrar.sol");
22+
const SchemeFactory = artifacts.require("./SchemeFactory.sol");
2223
const GenericScheme = artifacts.require("./GenericScheme.sol");
2324
const UpgradeScheme = artifacts.require("./UpgradeScheme.sol");
2425
const Auction4Reputation = artifacts.require("./Auction4Reputation.sol");
@@ -165,6 +166,7 @@ export const registrationAddVersionToPackege = async function (registration,vers
165166
registration.competition = await Competition.new();
166167
registration.contributionRewardExt = await ContributionRewardExt.new();
167168
registration.schemeRegistrar = await SchemeRegistrar.new();
169+
registration.schemeFactory = await SchemeFactory.new();
168170
registration.genericScheme = await GenericScheme.new();
169171
registration.upgradeScheme = await UpgradeScheme.new();
170172
registration.auction4Reputation = await Auction4Reputation.new();
@@ -190,6 +192,7 @@ export const registrationAddVersionToPackege = async function (registration,vers
190192
await implementationDirectory.setImplementation("Competition",registration.competition.address);
191193
await implementationDirectory.setImplementation("ContributionRewardExt",registration.contributionRewardExt.address);
192194
await implementationDirectory.setImplementation("SchemeRegistrar",registration.schemeRegistrar.address);
195+
await implementationDirectory.setImplementation("SchemeFactory",registration.schemeFactory.address);
193196
await implementationDirectory.setImplementation("GenericScheme",registration.genericScheme.address);
194197
await implementationDirectory.setImplementation("UpgradeScheme",registration.upgradeScheme.address);
195198
await implementationDirectory.setImplementation("Auction4Reputation",registration.auction4Reputation.address);

0 commit comments

Comments
 (0)