Skip to content

Commit

Permalink
DAOTracker: A Solution For Indexing New DAOs (#640)
Browse files Browse the repository at this point in the history
* init

* minor changes

* compiling

* tests passing

* DAOTracker tests

* linter fix

* updates based on feedback

* minimize storage

* merge

* lint fix

* pragma solidity ^0.5.11;
  • Loading branch information
dOrgJelli authored and orenyodfat committed Oct 5, 2019
1 parent fccac57 commit a7f50f2
Show file tree
Hide file tree
Showing 24 changed files with 375 additions and 49 deletions.
29 changes: 17 additions & 12 deletions contracts/universalSchemes/DaoCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ pragma solidity ^0.5.11;
import "./UniversalScheme.sol";
import "../controller/UController.sol";
import "../controller/Controller.sol";
import "../utils/DAOTracker.sol";


/**
* @title ControllerCreator for creating a single controller.
*/

contract ControllerCreator {

function create(Avatar _avatar) public returns(address) {
Expand All @@ -22,8 +22,6 @@ contract ControllerCreator {
/**
* @title Genesis Scheme that creates organizations
*/


contract DaoCreator {

mapping(address=>address) public locks;
Expand All @@ -32,9 +30,13 @@ contract DaoCreator {
event InitialSchemesSet (address _avatar);

ControllerCreator private controllerCreator;
DAOTracker private daoTracker;

constructor(ControllerCreator _controllerCreator) public {
constructor(ControllerCreator _controllerCreator, DAOTracker _daoTracker) public {
require(_controllerCreator != ControllerCreator(0));
require(_daoTracker != DAOTracker(0));
controllerCreator = _controllerCreator;
daoTracker = _daoTracker;
}

/**
Expand Down Expand Up @@ -198,16 +200,19 @@ contract DaoCreator {
// Create Controller:
if (UController(0) == _uController) {
controller = ControllerInterface(controllerCreator.create(avatar));
avatar.transferOwnership(address(controller));
// Transfer ownership:
nativeToken.transferOwnership(address(controller));
nativeReputation.transferOwnership(address(controller));
} else {
controller = _uController;
avatar.transferOwnership(address(controller));
// Transfer ownership:
nativeToken.transferOwnership(address(controller));
nativeReputation.transferOwnership(address(controller));
}

// Add the DAO to the tracking registry
daoTracker.track(avatar, controller);

// Transfer ownership:
avatar.transferOwnership(address(controller));
nativeToken.transferOwnership(address(controller));
nativeReputation.transferOwnership(address(controller));

if (controller == _uController) {
_uController.newOrganization(avatar);
}

Expand Down
90 changes: 90 additions & 0 deletions contracts/utils/DAOTracker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
pragma solidity ^0.5.11;

import "@daostack/infra/contracts/Reputation.sol";
import "../controller/DAOToken.sol";
import "../controller/Avatar.sol";
import "../controller/ControllerInterface.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";

/**
* @title An on-chain "source of truth" for what DAOs
* should be index into DAOstack's subgraph.
*/
contract DAOTracker is Ownable {

// `blacklist` the DAO from the subgraph's cache.
// Only able to be set by the owner of the DAOTracker.
mapping(address=>bool) public blacklisted;

event TrackDAO(address indexed _avatar, address _controller, address _reputation, address _daoToken);
event BlacklistDAO(address indexed _avatar, string _explanationHash);
event ResetDAO(address indexed _avatar, string _explanationHash);

modifier onlyAvatarOwner(Avatar avatar) {
require(avatar.owner() == msg.sender,
"The caller must be the owner of the Avatar.");
_;
}

modifier notBlacklisted(Avatar avatar) {
require(blacklisted[address(avatar)] == false,
"The avatar has been blacklisted.");
_;
}

/**
* @dev track a new organization. This function will tell the subgraph
* to start ingesting events from the DAO's contracts.
* NOTE: This function should be called as early as possible in the DAO deployment
* process. **Smart Contract Events that are emitted from blocks prior to this function's
* event being emitted WILL NOT be ingested into the subgraph**, leading to an incorrect
* cache. If this happens to you, please contact the subgraph maintainer. Your DAO will
* need to be added to the subgraph's startup config, and the cache will need to be rebuilt.
* @param _avatar the organization avatar
* @param _controller the organization controller
*/
function track(Avatar _avatar, ControllerInterface _controller)
public
onlyAvatarOwner(_avatar)
notBlacklisted(_avatar) {
// Only allow the information to be set once. In the case of a controller upgrades,
// the subgraph will be updated via the UpgradeController event.
require(_avatar != Avatar(0));
require(_controller != ControllerInterface(0));

emit TrackDAO(
address(_avatar),
address(_controller),
address(_avatar.nativeReputation()),
address(_avatar.nativeToken())
);
}

/**
* @dev blacklist a DAO from the cache. This should be callable by maintainer of the cache.
* Blacklisting can be used to defend against DoS attacks, or to remove spam. In order
* for this blacklisting to take affect within the cache, it would need to be rebuilt.
* @param _avatar the organization avatar
* @param _explanationHash A hash of a document explaining why this DAO was blacklisted
*/
function blacklist(Avatar _avatar, string memory _explanationHash)
public
onlyOwner {
require(_avatar != Avatar(0));
blacklisted[address(_avatar)] = true;
emit BlacklistDAO(address(_avatar), _explanationHash);
}

/**
* @dev reset a DAO in the cache. This should be callable by the maintainer of the cache.
* @param _avatar the organization avatar
* @param _explanationHash A hash of a document explaining why this DAO was reset
*/
function reset(Avatar _avatar, string memory _explanationHash)
public
onlyOwner {
require(_avatar != Avatar(0));
blacklisted[address(_avatar)] = false;
emit ResetDAO(address(_avatar), _explanationHash);
}
}
5 changes: 4 additions & 1 deletion migrations/2_deploy_organization.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var AbsoluteVote = artifacts.require('./AbsoluteVote.sol');
var ContributionReward = artifacts.require('./ContributionReward.sol');
var UpgradeScheme = artifacts.require('./UpgradeScheme.sol');
var ControllerCreator = artifacts.require('./ControllerCreator.sol');
var DAOTracker = artifacts.require('./DAOTracker.sol');
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';


Expand All @@ -35,8 +36,10 @@ var accounts;
//schemeRegistrar, upgradeScheme,globalConstraintRegistrar,simpleICO,contributionReward.
module.exports = async function(deployer) {
deployer.deploy(ControllerCreator, {gas: constants.ARC_GAS_LIMIT}).then(async function(){
await deployer.deploy(DAOTracker, {gas: constants.ARC_GAS_LIMIT});
var daoTracker = await DAOTracker.deployed();
var controllerCreator = await ControllerCreator.deployed();
await deployer.deploy(DaoCreator,controllerCreator.address, {gas: constants.ARC_GAS_LIMIT});
await deployer.deploy(DaoCreator,controllerCreator.address,daoTracker.address, {gas: constants.ARC_GAS_LIMIT});
var daoCreatorInst = await DaoCreator.deployed(controllerCreator.address,{gas: constants.ARC_GAS_LIMIT});
// Create DAOstack:

Expand Down
28 changes: 21 additions & 7 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion test/auction4reputation.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const helpers = require('./helpers');
const DaoCreator = artifacts.require("./DaoCreator.sol");
const ControllerCreator = artifacts.require("./ControllerCreator.sol");
const DAOTracker = artifacts.require("./DAOTracker.sol");
const constants = require('./constants');
const ERC20Mock = artifacts.require('./test/ERC20Mock.sol');
var Auction4Reputation = artifacts.require("./Auction4Reputation.sol");
Expand All @@ -17,7 +18,8 @@ const setup = async function (accounts,
var testSetup = new helpers.TestSetup();
testSetup.biddingToken = await ERC20Mock.new(accounts[0], web3.utils.toWei('100', "ether"));
var controllerCreator = await ControllerCreator.new({gas: constants.ARC_GAS_LIMIT});
testSetup.daoCreator = await DaoCreator.new(controllerCreator.address,{gas:constants.ARC_GAS_LIMIT});
var daoTracker = await DAOTracker.new({gas: constants.ARC_GAS_LIMIT});
testSetup.daoCreator = await DaoCreator.new(controllerCreator.address,daoTracker.address,{gas:constants.ARC_GAS_LIMIT});

testSetup.org = await helpers.setupOrganization(testSetup.daoCreator,accounts[0],1000,1000);
testSetup.auctionsEndTime = (await web3.eth.getBlock("latest")).timestamp + _auctionsEndTime;
Expand Down
4 changes: 3 additions & 1 deletion test/continuouslockingtoken4reputation.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const helpers = require('./helpers');
const DaoCreator = artifacts.require("./DaoCreator.sol");
const DAOTracker = artifacts.require("./DAOTracker.sol");
const ControllerCreator = artifacts.require("./ControllerCreator.sol");
const constants = require('./constants');
const ERC20Mock = artifacts.require('./test/ERC20Mock.sol');
Expand All @@ -21,7 +22,8 @@ const setup = async function (accounts,
var testSetup = new helpers.TestSetup();
testSetup.lockingToken = await ERC20Mock.new(accounts[0], web3.utils.toWei('100', "ether"));
var controllerCreator = await ControllerCreator.new({gas: constants.ARC_GAS_LIMIT});
testSetup.daoCreator = await DaoCreator.new(controllerCreator.address,{gas:constants.ARC_GAS_LIMIT});
var daoTracker = await DAOTracker.new({gas: constants.ARC_GAS_LIMIT});
testSetup.daoCreator = await DaoCreator.new(controllerCreator.address,daoTracker.address,{gas:constants.ARC_GAS_LIMIT});

testSetup.org = await helpers.setupOrganization(testSetup.daoCreator,accounts[0],1000,1000);
testSetup.startTime = (await web3.eth.getBlock("latest")).timestamp + _startTime;
Expand Down
4 changes: 3 additions & 1 deletion test/contributionreward.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const ContributionReward = artifacts.require("./ContributionReward.sol");
const ERC20Mock = artifacts.require('./test/ERC20Mock.sol');
const DaoCreator = artifacts.require("./DaoCreator.sol");
const ControllerCreator = artifacts.require("./ControllerCreator.sol");
const DAOTracker = artifacts.require("./DAOTracker.sol");
const Avatar = artifacts.require("./Avatar.sol");
const Redeemer = artifacts.require("./Redeemer.sol");

Expand Down Expand Up @@ -75,7 +76,8 @@ const setup = async function (accounts,genesisProtocol = false,tokenAddress=0) {
testSetup.standardTokenMock = await ERC20Mock.new(accounts[1],100);
testSetup.contributionReward = await ContributionReward.new();
var controllerCreator = await ControllerCreator.new({gas: constants.ARC_GAS_LIMIT});
testSetup.daoCreator = await DaoCreator.new(controllerCreator.address,{gas:constants.ARC_GAS_LIMIT});
var daoTracker = await DAOTracker.new({gas: constants.ARC_GAS_LIMIT});
testSetup.daoCreator = await DaoCreator.new(controllerCreator.address,daoTracker.address,{gas:constants.ARC_GAS_LIMIT});
if (genesisProtocol) {
testSetup.reputationArray = [1000,100,0];
} else {
Expand Down
7 changes: 5 additions & 2 deletions test/daocreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ const UController = artifacts.require("./UController.sol");
const ERC20Mock = artifacts.require('./test/ERC20Mock.sol');
const UniversalSchemeMock = artifacts.require('./test/UniversalSchemeMock.sol');
const ControllerCreator = artifacts.require("./ControllerCreator.sol");
const DAOTracker = artifacts.require("./DAOTracker.sol");

const zeroBytes32 = helpers.NULL_HASH;
var avatar,token,reputation,daoCreator,uController,controllerCreator;
const setup = async function (accounts,founderToken,founderReputation,useUController=false,cap=0) {
controllerCreator = await ControllerCreator.new({gas: constants.ARC_GAS_LIMIT});
daoCreator = await DaoCreator.new(controllerCreator.address,{gas:constants.ARC_GAS_LIMIT});
var daoTracker = await DAOTracker.new({gas: constants.ARC_GAS_LIMIT});
daoCreator = await DaoCreator.new(controllerCreator.address,daoTracker.address,{gas:constants.ARC_GAS_LIMIT});
var uControllerAddress = helpers.NULL_ADDRESS;
if (useUController){
uController = await UController.new({gas:constants.ARC_GAS_LIMIT});
Expand Down Expand Up @@ -289,7 +291,8 @@ contract('DaoCreator', function(accounts) {
it("forgeOrg with different params length should revert", async function() {
var amountToMint = 10;
var controllerCreator = await ControllerCreator.new({gas: constants.ARC_GAS_LIMIT});
daoCreator = await DaoCreator.new(controllerCreator.address,{gas:constants.ARC_GAS_LIMIT});
var daoTracker = await DAOTracker.new({gas: constants.ARC_GAS_LIMIT});
daoCreator = await DaoCreator.new(controllerCreator.address,daoTracker.address,{gas:constants.ARC_GAS_LIMIT});
var uControllerAddress = helpers.NULL_ADDRESS;
try {
await daoCreator.forgeOrg("testOrg","TEST","TST",[accounts[0]],[amountToMint],[],uControllerAddress,helpers.NULL_ADDRESS,{gas:constants.ARC_GAS_LIMIT});
Expand Down
Loading

0 comments on commit a7f50f2

Please sign in to comment.