├── .gitattributes ├── .gitignore ├── README.md ├── arapp.json ├── arapp_local.json ├── contracts ├── GardensTemplate.sol ├── Imports.sol └── external │ ├── IAragonFundraisingController.sol │ ├── IBancorMarketMaker.sol │ ├── IConvictionVoting.sol │ ├── ITollgate.sol │ └── Token.sol ├── package.json ├── scripts ├── deploy.js └── new-dao.js └── truffle.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | /.idea 4 | package-lock.json 5 | /flattened_contracts 6 | /abi -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gardens Template 2 | 3 | Aragon DAO Template to experiment with public community coordination. 4 | 5 | ## Local deployment 6 | 7 | To deploy a Gardens DAO to a local `aragon devchain`: 8 | 9 | 1) Install dependencies: 10 | ``` 11 | $ npm install 12 | ``` 13 | 14 | 2) In a separate console run Aragon Devchain: 15 | ``` 16 | $ npx aragon devchain 17 | ``` 18 | 19 | 3) From the output of the above copy the ENS address from "ENS instance deployed at:" to `arapp_local.json` `environments.devnet.registry` 20 | 21 | 4) In a separate console run start IPFS: 22 | ``` 23 | $ npx aragon ipfs start 24 | ``` 25 | 26 | 5) In a separate console run the Aragon Client: 27 | ``` 28 | $ npx aragon start 29 | ``` 30 | 31 | 6) Deploy the template with: 32 | ``` 33 | $ npm run deploy:rpc 34 | ``` 35 | 36 | 7) Deploy the Conviction Voting app to the devchain as it's not installed by default like the other main apps (Voting, Token Manager, Agent etc): 37 | - Download https://github.com/1Hive/conviction-voting-app 38 | - Run `npm install` in the root folder 39 | - Run `npm run build` in the root folder 40 | - Run `npx aragon apm publish major --files dist --skip-confirmation` in the root folder 41 | 42 | 8) Deploy the Dandelion Voting app to the devchain as it's not installed by default like the other main apps (Voting, Token Manager, Agent etc): 43 | - Download https://github.com/1Hive/dandelion-voting-app 44 | - Run `npm install` in the root folder 45 | - Run `npm run build` in the root folder 46 | - Run `npx aragon apm publish major --files dist --skip-confirmation` in the root folder 47 | 48 | 9) Deploy the Redemptions app to the devchain as it's not installed by default like the other main apps (Voting, Token Manager, Agent etc): 49 | - Download https://github.com/1Hive/redemptions-app 50 | - Run `npm install` in the root folder 51 | - Run `npm run build` in the root folder 52 | - Run `npx aragon apm publish major --files dist --skip-confirmation` in the root folder 53 | 54 | 10) Deploy the Tollgate app to the devchain as it's not installed by default like the other main apps (Voting, Token Manager, Agent etc): 55 | - Download https://github.com/aragonone/tollgate 56 | - Run `npm install` in the root folder 57 | - Run `npm run build` in the root folder 58 | - Run `npx aragon apm publish major --skip-confirmation` in the root folder 59 | 60 | 11) Deploy the Fundraising suite Presale app: 61 | - Download https://github.com/1Hive/aragon-fundraising 62 | - Run `npm install` in the `apps/presale` folder 63 | - Run `npx aragon apm publish major --skip-confirmation` in the `apps/presale` folder 64 | 65 | 12) Update the Fundraising app's UI (using the repo downloaded in the previous step): 66 | - Run `npm install` in the `apps/aragon-fundraising` folder 67 | - Run `npm run build` 68 | - Run `npx aragon apm publish major --files app/build --skip-confirmation` in the `apps/aragon-fundraising` folder 69 | 70 | 13) Deploy the Fundraising suite Bancor Marker Maker app (using the repo downloaded in step 11): 71 | - Run `npm install` in the `apps/bancor-market-maker` folder 72 | - Run `npx aragon apm publish major --skip-confirmation` in the `apps/bancor-market-maker` folder 73 | 74 | 14) Deploy the Hooked Token Manager app to the devchain as it's not installed by default like the other main apps 75 | (Voting, Token Manager, Agent etc): 76 | - Download https://github.com/1hive/token-manager-app/ 77 | - Run `npm install` in the root folder 78 | - Run `npm run build` in the root folder 79 | - Run `npx aragon apm publish major --files dist --skip-confirmation` in the root folder 80 | 81 | 15) Create a new Gardens Dao on the devchain: 82 | ``` 83 | $ npx truffle exec scripts/new-dao.js --network rpc 84 | ``` 85 | 86 | 16) Copy the output DAO address into this URL and open it in a web browser: 87 | ``` 88 | http://localhost:3000/#/ 89 | ``` 90 | 91 | 92 | ## Rinkeby deployment using previously deployed template 93 | 94 | To deploy a Gardens DAO to Rinkeby: 95 | 96 | 1) Install dependencies: 97 | ``` 98 | $ npm install 99 | ``` 100 | 101 | 2) Compile contracts: 102 | ``` 103 | $ npx truffle compile 104 | ``` 105 | 106 | 3) Configure your DAO in: `scripts/new-dao.js` 107 | 108 | 4) Deploy a DAO to Rinkeby (requires a Rinkeby account accessible by the truffle script as documented here: 109 | https://hack.aragon.org/docs/cli-intro#set-a-private-key): 110 | ``` 111 | $ npx truffle exec scripts/new-dao.js --network rinkeby 112 | ``` 113 | 114 | 5) Copy the output DAO address into this URL and open it in a web browser: 115 | ``` 116 | https://rinkeby.aragon.org/#/ 117 | ``` 118 | -------------------------------------------------------------------------------- /arapp.json: -------------------------------------------------------------------------------- 1 | { 2 | "environments": { 3 | "default": { 4 | "appName": "gardens-template.aragonpm.eth", 5 | "network": "rpc" 6 | }, 7 | "devnet": { 8 | "appName": "gardens-template.aragonpm.eth", 9 | "network": "devnet" 10 | }, 11 | "rinkeby": { 12 | "appName": "gardens-template.aragonpm.eth", 13 | "address": "0xe1a95f2941cf6190845290c4fe4c17208a8b395d", 14 | "network": "rinkeby", 15 | "registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D" 16 | } 17 | }, 18 | "path": "contracts/GardensTemplate.sol" 19 | } -------------------------------------------------------------------------------- /arapp_local.json: -------------------------------------------------------------------------------- 1 | { 2 | "environments": { 3 | "devnet": { 4 | "appName": "gardens-template.aragonpm.eth", 5 | "address": "0xb6afdc6f563557b2722c283d28969ca7fdc53bdc", 6 | "network": "devnet", 7 | "registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1" 8 | } 9 | }, 10 | "path": "contracts/GardensTemplate.sol" 11 | } -------------------------------------------------------------------------------- /contracts/GardensTemplate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "@aragon/templates-shared/contracts/BaseTemplate.sol"; 4 | import "@1hive/apps-dandelion-voting/contracts/DandelionVoting.sol"; 5 | import "@1hive/apps-redemptions/contracts/Redemptions.sol"; 6 | import "@1hive/apps-token-manager/contracts/HookedTokenManager.sol"; 7 | import "@ablack/fundraising-bancor-formula/contracts/BancorFormula.sol"; 8 | import "@ablack/fundraising-aragon-fundraising/contracts/AragonFundraisingController.sol"; 9 | import "@ablack/fundraising-presale/contracts/Presale.sol"; 10 | import {IConvictionVoting as ConvictionVoting} from "./external/IConvictionVoting.sol"; 11 | import {ITollgate as Tollgate} from "./external/ITollgate.sol"; 12 | import {IBancorMarketMaker as MarketMaker} from "./external/IBancorMarketMaker.sol"; 13 | import {IAragonFundraisingController as Controller} from "./external/IAragonFundraisingController.sol"; 14 | 15 | contract GardensTemplate is BaseTemplate { 16 | 17 | string constant private ERROR_MISSING_MEMBERS = "MISSING_MEMBERS"; 18 | string constant private ERROR_BAD_VOTE_SETTINGS = "BAD_SETTINGS"; 19 | string constant private ERROR_NO_CACHE = "NO_CACHE"; 20 | string constant private ERROR_NO_COLLATERAL = "NO_COLLATERAL"; 21 | string constant private ERROR_NO_TOLLGATE_TOKEN = "NO_TOLLGATE_TOKEN"; 22 | 23 | /** 24 | * bytes32 private constant DANDELION_VOTING_APP_ID = apmNamehash("dandelion-voting"); 25 | * bytes32 private constant REDEMPTIONS_APP_ID = keccak256(abi.encodePacked(apmNamehash("open"), keccak256("redemptions"))); 26 | * bytes32 private constant CONVICTION_VOTING_APP_ID = keccak256(abi.encodePacked(apmNamehash("open"), keccak256("conviction-voting"))); 27 | * bytes32 private constant TOLLGATE_APP_ID = apmNamehash("tollgate"); 28 | * bytes32 private constant HOOKED_TOKEN_MANAGER_APP_ID = keccak256(abi.encodePacked(apmNamehash("open"), keccak256("hooked-token-manager"))); 29 | * bytes32 private constant BANCOR_FORMULA_ID = apmNamehash("bancor-formula"); 30 | * bytes32 private constant PRESALE_ID = apmNamehash("presale"); 31 | * bytes32 private constant MARKET_MAKER_ID = keccak256(abi.encodePacked(apmNamehash("open"), keccak256("bancor-market-maker"))); 32 | * bytes32 private constant MARKETPLACE_CONTROLLER_ID = keccak256(abi.encodePacked(apmNamehash("open"), keccak256("marketplace-controller"))); 33 | */ 34 | // Local app ID's 35 | bytes32 private constant DANDELION_VOTING_APP_ID = 0x2d7442e1c4cb7a7013aecc419f938bdfa55ad32d90002fb92ee5969e27b2bf07; 36 | bytes32 private constant REDEMPTIONS_APP_ID = 0xbf9fefd9508fe20f068aa3714e9beb8c4fc6dea33dc4c75371b96140d6350d20; 37 | bytes32 private constant CONVICTION_VOTING_APP_ID = 0x589851b3734f6578a92f33bfc26877a1166b95238be1f484deeaac6383d14c38; 38 | bytes32 private constant TOLLGATE_APP_ID = 0x7075e547e73484f0736b2160fcfb010b4f32b751fc729c25b677a0347d9b4246; 39 | bytes32 private constant HOOKED_TOKEN_MANAGER_APP_ID = 0xb2d2065b829a91588c8b9a15d99acd026f6673733603c6c60e505654eb2b472d; 40 | 41 | // Rinkeby app ID's 42 | // bytes32 private constant DANDELION_VOTING_APP_ID = 0x40a80c4b4050993512df39c802adec62dafeb1f0586cc15f4d34bda9c47ba468; // gardens-dandelion-voting.open.aragonpm.eth 43 | // bytes32 private constant REDEMPTIONS_APP_ID = 0x743bd419d5c9061290b181b19e114f36e9cc9ddb42b4e54fc811edb22eb85e9d; 44 | // bytes32 private constant TOLLGATE_APP_ID = 0x0d321283289e70165ef6db7f11fc62c74a7d39dac3ee148428c4f9e3d74c6d61; // tollgate.open.aragonpm.eth 45 | // bytes32 private constant HOOKED_TOKEN_MANAGER_APP_ID = 0x26bb91b115bf14acbdc18d75042e165321eceeb3d10d852386576bbd0ec11519; // gardens-token-manager.open.aragonpm.eth 46 | 47 | // bytes32 private constant CONVICTION_VOTING_APP_ID = 0xc5e2e4d7422daecfc2dc08a9932e5b9f84cf5b2f61a81ef13e257b4803b93b2f; // gardens-conviction-voting.open.aragonpm.eth 48 | // bytes32 private constant CONVICTION_VOTING_APP_ID = 0x2e20d4e6af7ec3f285a8c7358e357d14483ccf38e79779c3037bc70c24141038; // conviction-voting-gardens.open.aragonpm.eth 49 | 50 | // Local and Rinkeby app ID's (no need to change between environments) 51 | bytes32 private constant BANCOR_FORMULA_ID = 0xd71dde5e4bea1928026c1779bde7ed27bd7ef3d0ce9802e4117631eb6fa4ed7d; 52 | bytes32 private constant PRESALE_ID = 0x5de9bbdeaf6584c220c7b7f1922383bcd8bbcd4b48832080afd9d5ebf9a04df5; 53 | bytes32 private constant MARKET_MAKER_ID = 0x082190c7d2371cc21a38fcd9c3cd724dd686e838e1f7a435f9f5fbc0cb562f0f; // bancor-market-maker.open.aragonpm.eth 54 | bytes32 private constant MARKETPLACE_CONTROLLER_ID = 0x11a4d82f2764c41dac72ca406a52a7392a0800047a040944b29dccd669bad8e9; // marketplace-controller.open.aragonpm.eth 55 | 56 | bool private constant TOKEN_TRANSFERABLE = true; 57 | uint8 private constant TOKEN_DECIMALS = uint8(18); 58 | uint256 private constant TOKEN_MAX_PER_ACCOUNT = uint256(-1); 59 | uint64 private constant DEFAULT_FINANCE_PERIOD = uint64(30 days); 60 | address private constant ANY_ENTITY = address(-1); 61 | uint8 private constant ORACLE_PARAM_ID = 203; 62 | enum Op { NONE, EQ, NEQ, GT, LT, GTE, LTE, RET, NOT, AND, OR, XOR, IF_ELSE } 63 | 64 | struct DeployedContracts { 65 | Kernel dao; 66 | ACL acl; 67 | DandelionVoting dandelionVoting; 68 | Vault fundingPoolVault; 69 | HookedTokenManager hookedTokenManager; 70 | address collateralToken; 71 | Vault reserveVault; 72 | Presale presale; 73 | MarketMaker marketMaker; 74 | Controller controller; 75 | } 76 | 77 | mapping(address => DeployedContracts) internal senderDeployedContracts; 78 | 79 | constructor(DAOFactory _daoFactory, ENS _ens, MiniMeTokenFactory _miniMeFactory, IFIFSResolvingRegistrar _aragonID) 80 | BaseTemplate(_daoFactory, _ens, _miniMeFactory, _aragonID) 81 | public 82 | { 83 | _ensureAragonIdIsValid(_aragonID); 84 | _ensureMiniMeFactoryIsValid(_miniMeFactory); 85 | } 86 | 87 | // New DAO functions // 88 | 89 | /** 90 | * @dev Create the DAO and initialise the basic apps necessary for gardens 91 | * @param _voteTokenName The name for the token used by share holders in the organization 92 | * @param _voteTokenSymbol The symbol for the token used by share holders in the organization 93 | * @param _votingSettings Array of [supportRequired, minAcceptanceQuorum, voteDuration, voteBufferBlocks, voteExecutionDelayBlocks] to set up the voting app of the organization 94 | * @param _useAgentAsVault Whether to use an Agent app or Vault app 95 | */ 96 | function createDaoTxOne( 97 | string _voteTokenName, 98 | string _voteTokenSymbol, 99 | uint64[5] _votingSettings, 100 | bool _useAgentAsVault 101 | ) 102 | public 103 | { 104 | require(_votingSettings.length == 5, ERROR_BAD_VOTE_SETTINGS); 105 | 106 | (Kernel dao, ACL acl) = _createDAO(); 107 | MiniMeToken voteToken = _createToken(_voteTokenName, _voteTokenSymbol, TOKEN_DECIMALS); 108 | Vault fundingPoolVault = _useAgentAsVault ? _installDefaultAgentApp(dao) : _installVaultApp(dao); 109 | DandelionVoting dandelionVoting = _installDandelionVotingApp(dao, voteToken, _votingSettings); 110 | HookedTokenManager hookedTokenManager = _installHookedTokenManagerApp(dao, voteToken, TOKEN_TRANSFERABLE, TOKEN_MAX_PER_ACCOUNT); 111 | 112 | if (_useAgentAsVault) { 113 | _createAgentPermissions(acl, Agent(fundingPoolVault), dandelionVoting, dandelionVoting); 114 | } 115 | _createEvmScriptsRegistryPermissions(acl, dandelionVoting, dandelionVoting); 116 | _createCustomVotingPermissions(acl, dandelionVoting, hookedTokenManager); 117 | 118 | _storeDeployedContractsTxOne(dao, acl, dandelionVoting, fundingPoolVault, hookedTokenManager); 119 | } 120 | 121 | /** 122 | * @dev Add and initialise tollgate, redemptions and conviction voting or finance apps 123 | * @param _tollgateFeeToken The token used to pay the tollgate fee 124 | * @param _tollgateFeeAmount The tollgate fee amount 125 | * @param _redeemableTokens Array of initially redeemable tokens 126 | * @param _useConvictionAsFinance Whether to use conviction voting or finance 127 | * @param _financePeriod Finance period 128 | * @param _collateralToken Token distributed by conviction voting and used as collateral in fundraising 129 | */ 130 | function createDaoTxTwo( 131 | ERC20 _tollgateFeeToken, 132 | uint256 _tollgateFeeAmount, 133 | address[] _redeemableTokens, 134 | bool _useConvictionAsFinance, 135 | uint64 _financePeriod, 136 | address _collateralToken 137 | ) 138 | public 139 | { 140 | require(_tollgateFeeToken != address(0), ERROR_NO_TOLLGATE_TOKEN); 141 | require(_collateralToken != address(0), ERROR_NO_COLLATERAL); 142 | require(senderDeployedContracts[msg.sender].dao != address(0), ERROR_NO_CACHE); 143 | 144 | (, 145 | ACL acl, 146 | DandelionVoting dandelionVoting, 147 | Vault fundingPoolVault, 148 | HookedTokenManager hookedTokenManager) = _getDeployedContractsTxOne(); 149 | 150 | Tollgate tollgate = _installTollgate(senderDeployedContracts[msg.sender].dao, _tollgateFeeToken, _tollgateFeeAmount, address(fundingPoolVault)); 151 | _createTollgatePermissions(acl, tollgate, dandelionVoting); 152 | 153 | Redemptions redemptions = _installRedemptions(senderDeployedContracts[msg.sender].dao, fundingPoolVault, hookedTokenManager, _redeemableTokens); 154 | _createRedemptionsPermissions(acl, redemptions, dandelionVoting); 155 | 156 | if (_useConvictionAsFinance) { 157 | ConvictionVoting convictionVoting = _installConvictionVoting(senderDeployedContracts[msg.sender].dao, hookedTokenManager.token(), fundingPoolVault, _collateralToken); 158 | _createVaultPermissions(acl, fundingPoolVault, convictionVoting, dandelionVoting); 159 | _createConvictionVotingPermissions(acl, convictionVoting, dandelionVoting); 160 | 161 | _createPermissionForTemplate(acl, hookedTokenManager, hookedTokenManager.SET_HOOK_ROLE()); 162 | hookedTokenManager.registerHook(convictionVoting); 163 | hookedTokenManager.registerHook(dandelionVoting); 164 | _removePermissionFromTemplate(acl, hookedTokenManager, hookedTokenManager.SET_HOOK_ROLE()); 165 | } else { 166 | Finance finance = _installFinanceApp(senderDeployedContracts[msg.sender].dao, fundingPoolVault, _financePeriod == 0 ? DEFAULT_FINANCE_PERIOD : _financePeriod); 167 | _createVaultPermissions(acl, fundingPoolVault, finance, dandelionVoting); 168 | _createFinancePermissions(acl, finance, dandelionVoting, dandelionVoting); 169 | } 170 | 171 | _storeDeployedContractsTxTwo(_collateralToken); 172 | } 173 | 174 | /** 175 | * @dev Add and initialise fundraising apps 176 | * @param _goal Presale goal in wei 177 | * @param _period Presale length in seconds 178 | * @param _exchangeRate Presale exchange rate in PPM 179 | * @param _vestingCliffPeriod Vesting cliff length for presale bought tokens in seconds 180 | * @param _vestingCompletePeriod Vesting complete length for presale bought tokens in seconds 181 | * @param _supplyOfferedPct Percent of total supply offered in presale in PPM 182 | * @param _fundingForBeneficiaryPct Percent of raised presale funds sent to the organization in PPM 183 | * @param _openDate The time the presale starts, requires manual opening if set to 0 184 | * @param _buyFeePct The Percent of a purchase contribution that is sent to the organization in PCT_BASE 185 | * @param _sellFeePct The percent of a sale return that is sent to the organization in PCT_BASE 186 | */ 187 | function createDaoTxThree( 188 | uint256 _goal, 189 | uint64 _period, 190 | uint256 _exchangeRate, 191 | uint64 _vestingCliffPeriod, 192 | uint64 _vestingCompletePeriod, 193 | uint256 _supplyOfferedPct, 194 | uint256 _fundingForBeneficiaryPct, 195 | uint64 _openDate, 196 | uint256 _buyFeePct, 197 | uint256 _sellFeePct 198 | ) 199 | public 200 | { 201 | require(senderDeployedContracts[msg.sender].collateralToken != address(0), ERROR_NO_CACHE); 202 | 203 | _installFundraisingApps( 204 | _goal, 205 | _period, 206 | _exchangeRate, 207 | _vestingCliffPeriod, 208 | _vestingCompletePeriod, 209 | _supplyOfferedPct, 210 | _fundingForBeneficiaryPct, 211 | _openDate, 212 | _buyFeePct, 213 | _sellFeePct 214 | ); 215 | 216 | _createHookedTokenManagerPermissions(); 217 | _createFundraisingPermissions(); 218 | } 219 | 220 | /** 221 | * @dev Configure the fundraising collateral and finalise permissions 222 | * @param _id Unique Aragon DAO ID 223 | * @param _virtualSupply Collateral token virtual supply in wei 224 | * @param _virtualBalance Collateral token virtual balance in wei 225 | * @param _reserveRatio The reserve ratio to be used for the collateral token in PPM 226 | */ 227 | function createDaoTxFour( 228 | string _id, 229 | uint256 _virtualSupply, 230 | uint256 _virtualBalance, 231 | uint32 _reserveRatio 232 | ) 233 | public 234 | { 235 | require(senderDeployedContracts[msg.sender].reserveVault != address(0), ERROR_NO_CACHE); 236 | 237 | _validateId(_id); 238 | (Kernel dao, ACL acl, DandelionVoting dandelionVoting,,) = _getDeployedContractsTxOne(); 239 | 240 | _setupCollateralToken(dao, acl, _virtualSupply, _virtualBalance, _reserveRatio); 241 | 242 | _transferRootPermissionsFromTemplateAndFinalizeDAO(dao, dandelionVoting); 243 | _registerID(_id, dao); 244 | _deleteStoredContracts(); 245 | } 246 | 247 | // App installation/setup functions // 248 | 249 | function _installHookedTokenManagerApp( 250 | Kernel _dao, 251 | MiniMeToken _token, 252 | bool _transferable, 253 | uint256 _maxAccountTokens 254 | ) 255 | internal returns (HookedTokenManager) 256 | { 257 | HookedTokenManager hookedTokenManager = HookedTokenManager(_installDefaultApp(_dao, HOOKED_TOKEN_MANAGER_APP_ID)); 258 | _token.changeController(hookedTokenManager); 259 | hookedTokenManager.initialize(_token, _transferable, _maxAccountTokens); 260 | return hookedTokenManager; 261 | } 262 | 263 | function _installDandelionVotingApp(Kernel _dao, MiniMeToken _voteToken, uint64[5] _votingSettings) 264 | internal returns (DandelionVoting) 265 | { 266 | DandelionVoting dandelionVoting = DandelionVoting(_installNonDefaultApp(_dao, DANDELION_VOTING_APP_ID)); 267 | dandelionVoting.initialize(_voteToken, _votingSettings[0], _votingSettings[1], _votingSettings[2], 268 | _votingSettings[3], _votingSettings[4]); 269 | return dandelionVoting; 270 | } 271 | 272 | function _installTollgate(Kernel _dao, ERC20 _tollgateFeeToken, uint256 _tollgateFeeAmount, address _tollgateFeeDestination) 273 | internal returns (Tollgate) 274 | { 275 | Tollgate tollgate = Tollgate(_installNonDefaultApp(_dao, TOLLGATE_APP_ID)); 276 | tollgate.initialize(_tollgateFeeToken, _tollgateFeeAmount, _tollgateFeeDestination); 277 | return tollgate; 278 | } 279 | 280 | function _installRedemptions(Kernel _dao, Vault _agentOrVault, HookedTokenManager _hookedTokenManager, address[] _redeemableTokens) 281 | internal returns (Redemptions) 282 | { 283 | Redemptions redemptions = Redemptions(_installNonDefaultApp(_dao, REDEMPTIONS_APP_ID)); 284 | redemptions.initialize(_agentOrVault, TokenManager(_hookedTokenManager), _redeemableTokens); 285 | return redemptions; 286 | } 287 | 288 | function _installConvictionVoting(Kernel _dao, MiniMeToken _stakeToken, Vault _agentOrVault, address _requestToken) 289 | internal returns (ConvictionVoting) 290 | { 291 | ConvictionVoting convictionVoting = ConvictionVoting(_installNonDefaultApp(_dao, CONVICTION_VOTING_APP_ID)); 292 | convictionVoting.initialize(_stakeToken, _agentOrVault, _requestToken); 293 | return convictionVoting; 294 | } 295 | 296 | function _installFundraisingApps( 297 | uint256 _goal, 298 | uint64 _period, 299 | uint256 _exchangeRate, 300 | uint64 _vestingCliffPeriod, 301 | uint64 _vestingCompletePeriod, 302 | uint256 _supplyOfferedPct, 303 | uint256 _fundingForBeneficiaryPct, 304 | uint64 _openDate, 305 | uint256 _buyFeePct, 306 | uint256 _sellFeePct 307 | ) 308 | internal 309 | { 310 | _proxifyFundraisingApps(); 311 | address collateralToken = _getDeployedContractsTxTwo(); 312 | 313 | _initializePresale( 314 | _goal, 315 | _period, 316 | _exchangeRate, 317 | _vestingCliffPeriod, 318 | _vestingCompletePeriod, 319 | _supplyOfferedPct, 320 | _fundingForBeneficiaryPct, 321 | _openDate, 322 | collateralToken 323 | ); 324 | _initializeMarketMaker(_buyFeePct, _sellFeePct); 325 | _initializeController(collateralToken); 326 | } 327 | 328 | function _proxifyFundraisingApps() internal { 329 | (Kernel dao,,,,) = _getDeployedContractsTxOne(); 330 | 331 | Vault reserveVault = _installVaultApp(dao); 332 | Presale presale = Presale(_installNonDefaultApp(dao, PRESALE_ID)); 333 | MarketMaker marketMaker = MarketMaker(_installNonDefaultApp(dao, MARKET_MAKER_ID)); 334 | Controller controller = Controller(_installNonDefaultApp(dao, MARKETPLACE_CONTROLLER_ID)); 335 | 336 | _storeDeployedContractsTxThree(reserveVault, presale, marketMaker, controller); 337 | } 338 | 339 | function _initializePresale( 340 | uint256 _goal, 341 | uint64 _period, 342 | uint256 _exchangeRate, 343 | uint64 _vestingCliffPeriod, 344 | uint64 _vestingCompletePeriod, 345 | uint256 _supplyOfferedPct, 346 | uint256 _fundingForBeneficiaryPct, 347 | uint64 _openDate, 348 | address _collateralToken 349 | ) 350 | internal 351 | { 352 | // Accessing deployed contracts directly due to stack too deep error. 353 | senderDeployedContracts[msg.sender].presale.initialize( 354 | AragonFundraisingController(senderDeployedContracts[msg.sender].controller), 355 | TokenManager(senderDeployedContracts[msg.sender].hookedTokenManager), 356 | senderDeployedContracts[msg.sender].reserveVault, 357 | senderDeployedContracts[msg.sender].fundingPoolVault, 358 | _collateralToken, 359 | _goal, 360 | _period, 361 | _exchangeRate, 362 | _vestingCliffPeriod, 363 | _vestingCompletePeriod, 364 | _supplyOfferedPct, 365 | _fundingForBeneficiaryPct, 366 | _openDate 367 | ); 368 | } 369 | 370 | function _initializeMarketMaker(uint256 _buyFeePct, uint256 _sellFeePct) internal { 371 | IBancorFormula bancorFormula = IBancorFormula(_latestVersionAppBase(BANCOR_FORMULA_ID)); 372 | 373 | (,,, Vault beneficiary, HookedTokenManager hookedTokenManager) = _getDeployedContractsTxOne(); 374 | (Vault reserveVault,, MarketMaker marketMaker, Controller controller) = _getDeployedContractsTxThree(); 375 | 376 | marketMaker.initialize(AragonFundraisingController(controller), TokenManager(hookedTokenManager), bancorFormula, reserveVault, beneficiary, _buyFeePct, _sellFeePct); 377 | } 378 | 379 | function _initializeController(address _collateralToken) internal { 380 | (Vault reserveVault, Presale presale, MarketMaker marketMaker, Controller controller) = _getDeployedContractsTxThree(); 381 | controller.initialize(presale, marketMaker, reserveVault); 382 | } 383 | 384 | function _setupCollateralToken( 385 | Kernel _dao, 386 | ACL _acl, 387 | uint256 _virtualSupply, 388 | uint256 _virtualBalance, 389 | uint32 _reserveRatio 390 | ) 391 | internal 392 | { 393 | (,, DandelionVoting dandelionVoting,,) = _getDeployedContractsTxOne(); 394 | (,,, Controller controller) = _getDeployedContractsTxThree(); 395 | address collateralToken = _getDeployedContractsTxTwo(); 396 | 397 | _createPermissionForTemplate(_acl, address(controller), controller.ADD_COLLATERAL_TOKEN_ROLE()); 398 | controller.addCollateralToken( 399 | collateralToken, 400 | _virtualSupply, 401 | _virtualBalance, 402 | _reserveRatio 403 | ); 404 | _transferPermissionFromTemplate(_acl, controller, dandelionVoting, controller.ADD_COLLATERAL_TOKEN_ROLE(), dandelionVoting); 405 | } 406 | 407 | // Permission setting functions // 408 | 409 | function _createCustomVotingPermissions(ACL _acl, DandelionVoting _dandelionVoting, HookedTokenManager _hookedTokenManager) 410 | internal 411 | { 412 | _acl.createPermission(_dandelionVoting, _dandelionVoting, _dandelionVoting.MODIFY_QUORUM_ROLE(), _dandelionVoting); 413 | _acl.createPermission(_dandelionVoting, _dandelionVoting, _dandelionVoting.MODIFY_SUPPORT_ROLE(), _dandelionVoting); 414 | _acl.createPermission(_dandelionVoting, _dandelionVoting, _dandelionVoting.MODIFY_BUFFER_BLOCKS_ROLE(), _dandelionVoting); 415 | _acl.createPermission(_dandelionVoting, _dandelionVoting, _dandelionVoting.MODIFY_EXECUTION_DELAY_ROLE(), _dandelionVoting); 416 | } 417 | 418 | function _createTollgatePermissions(ACL _acl, Tollgate _tollgate, DandelionVoting _dandelionVoting) internal { 419 | _acl.createPermission(_dandelionVoting, _tollgate, _tollgate.CHANGE_AMOUNT_ROLE(), _dandelionVoting); 420 | _acl.createPermission(_dandelionVoting, _tollgate, _tollgate.CHANGE_DESTINATION_ROLE(), _dandelionVoting); 421 | _acl.createPermission(_tollgate, _dandelionVoting, _dandelionVoting.CREATE_VOTES_ROLE(), _dandelionVoting); 422 | } 423 | 424 | function _createRedemptionsPermissions(ACL _acl, Redemptions _redemptions, DandelionVoting _dandelionVoting) 425 | internal 426 | { 427 | _acl.createPermission(ANY_ENTITY, _redemptions, _redemptions.REDEEM_ROLE(), address(this)); 428 | _setOracle(_acl, ANY_ENTITY, _redemptions, _redemptions.REDEEM_ROLE(), _dandelionVoting); 429 | _acl.setPermissionManager(_dandelionVoting, _redemptions, _redemptions.REDEEM_ROLE()); 430 | 431 | _acl.createPermission(_dandelionVoting, _redemptions, _redemptions.ADD_TOKEN_ROLE(), _dandelionVoting); 432 | _acl.createPermission(_dandelionVoting, _redemptions, _redemptions.REMOVE_TOKEN_ROLE(), _dandelionVoting); 433 | } 434 | 435 | function _createConvictionVotingPermissions(ACL _acl, ConvictionVoting _convictionVoting, DandelionVoting _dandelionVoting) 436 | internal 437 | { 438 | _acl.createPermission(ANY_ENTITY, _convictionVoting, _convictionVoting.CREATE_PROPOSALS_ROLE(), _dandelionVoting); 439 | } 440 | 441 | function _createHookedTokenManagerPermissions() internal { 442 | (, ACL acl,,,) = _getDeployedContractsTxOne(); 443 | (,, DandelionVoting dandelionVoting,, HookedTokenManager hookedTokenManager) = _getDeployedContractsTxOne(); 444 | (, Presale presale, MarketMaker marketMaker,) = _getDeployedContractsTxThree(); 445 | 446 | address[] memory grantees = new address[](2); 447 | grantees[0] = address(marketMaker); 448 | grantees[1] = address(presale); 449 | acl.createPermission(marketMaker, hookedTokenManager, hookedTokenManager.MINT_ROLE(), dandelionVoting); 450 | acl.createPermission(presale, hookedTokenManager, hookedTokenManager.ISSUE_ROLE(), dandelionVoting); 451 | acl.createPermission(presale, hookedTokenManager, hookedTokenManager.ASSIGN_ROLE(), dandelionVoting); 452 | acl.createPermission(presale, hookedTokenManager, hookedTokenManager.REVOKE_VESTINGS_ROLE(), dandelionVoting); 453 | _createPermissions(acl, grantees, hookedTokenManager, hookedTokenManager.BURN_ROLE(), dandelionVoting); 454 | } 455 | 456 | function _createFundraisingPermissions() internal { 457 | (, ACL acl,,,) = _getDeployedContractsTxOne(); 458 | (,, DandelionVoting dandelionVoting,,) = _getDeployedContractsTxOne(); 459 | (Vault reserveVault, Presale presale, MarketMaker marketMaker, Controller controller) = _getDeployedContractsTxThree(); 460 | 461 | // reserveVault 462 | acl.createPermission(marketMaker, reserveVault, reserveVault.TRANSFER_ROLE(), dandelionVoting); 463 | // presale 464 | acl.createPermission(controller, presale, presale.OPEN_ROLE(), dandelionVoting); 465 | acl.createPermission(controller, presale, presale.CONTRIBUTE_ROLE(), dandelionVoting); 466 | // market maker 467 | acl.createPermission(controller, marketMaker, marketMaker.CONTROLLER_ROLE(), dandelionVoting); 468 | // controller 469 | // ADD_COLLATERAL_TOKEN_ROLE is handled later [after collaterals have been added] 470 | acl.createPermission(dandelionVoting, controller, controller.UPDATE_BENEFICIARY_ROLE(), dandelionVoting); 471 | acl.createPermission(dandelionVoting, controller, controller.UPDATE_FEES_ROLE(), dandelionVoting); 472 | acl.createPermission(dandelionVoting, controller, controller.REMOVE_COLLATERAL_TOKEN_ROLE(), dandelionVoting); 473 | acl.createPermission(dandelionVoting, controller, controller.UPDATE_COLLATERAL_TOKEN_ROLE(), dandelionVoting); 474 | acl.createPermission(ANY_ENTITY, controller, controller.OPEN_PRESALE_ROLE(), dandelionVoting); 475 | acl.createPermission(presale, controller, controller.OPEN_TRADING_ROLE(), dandelionVoting); 476 | acl.createPermission(ANY_ENTITY, controller, controller.CONTRIBUTE_ROLE(), dandelionVoting); 477 | acl.createPermission(ANY_ENTITY, controller, controller.MAKE_BUY_ORDER_ROLE(), dandelionVoting); 478 | acl.createPermission(ANY_ENTITY, controller, controller.MAKE_SELL_ORDER_ROLE(), address(this)); 479 | _setOracle(acl, ANY_ENTITY, controller, controller.MAKE_SELL_ORDER_ROLE(), dandelionVoting); 480 | acl.setPermissionManager(dandelionVoting, controller, controller.MAKE_SELL_ORDER_ROLE()); 481 | } 482 | 483 | // Temporary Storage functions // 484 | 485 | function _storeDeployedContractsTxOne(Kernel _dao, ACL _acl, DandelionVoting _dandelionVoting, Vault _agentOrVault, HookedTokenManager _hookedTokenManager) 486 | internal 487 | { 488 | DeployedContracts storage deployedContracts = senderDeployedContracts[msg.sender]; 489 | deployedContracts.dao = _dao; 490 | deployedContracts.acl = _acl; 491 | deployedContracts.dandelionVoting = _dandelionVoting; 492 | deployedContracts.fundingPoolVault = _agentOrVault; 493 | deployedContracts.hookedTokenManager = _hookedTokenManager; 494 | } 495 | 496 | function _getDeployedContractsTxOne() internal returns (Kernel, ACL, DandelionVoting, Vault, HookedTokenManager) { 497 | DeployedContracts storage deployedContracts = senderDeployedContracts[msg.sender]; 498 | return ( 499 | deployedContracts.dao, 500 | deployedContracts.acl, 501 | deployedContracts.dandelionVoting, 502 | deployedContracts.fundingPoolVault, 503 | deployedContracts.hookedTokenManager 504 | ); 505 | } 506 | 507 | function _storeDeployedContractsTxTwo(address _collateralToken) internal { 508 | DeployedContracts storage deployedContracts = senderDeployedContracts[msg.sender]; 509 | deployedContracts.collateralToken = _collateralToken; 510 | } 511 | 512 | function _getDeployedContractsTxTwo() internal returns (address) { 513 | DeployedContracts storage deployedContracts = senderDeployedContracts[msg.sender]; 514 | return deployedContracts.collateralToken; 515 | } 516 | 517 | function _storeDeployedContractsTxThree(Vault _reserve, Presale _presale, MarketMaker _marketMaker, Controller _controller) 518 | internal 519 | { 520 | DeployedContracts storage deployedContracts = senderDeployedContracts[msg.sender]; 521 | deployedContracts.reserveVault = _reserve; 522 | deployedContracts.presale = _presale; 523 | deployedContracts.marketMaker = _marketMaker; 524 | deployedContracts.controller = _controller; 525 | } 526 | 527 | function _getDeployedContractsTxThree() internal returns (Vault, Presale, MarketMaker, Controller) { 528 | DeployedContracts storage deployedContracts = senderDeployedContracts[msg.sender]; 529 | return ( 530 | deployedContracts.reserveVault, 531 | deployedContracts.presale, 532 | deployedContracts.marketMaker, 533 | deployedContracts.controller 534 | ); 535 | } 536 | 537 | function _deleteStoredContracts() internal { 538 | delete senderDeployedContracts[msg.sender]; 539 | } 540 | 541 | // Oracle permissions with params functions // 542 | 543 | function _setOracle(ACL _acl, address _who, address _where, bytes32 _what, address _oracle) private { 544 | uint256[] memory params = new uint256[](1); 545 | params[0] = _paramsTo256(ORACLE_PARAM_ID, uint8(Op.EQ), uint240(_oracle)); 546 | 547 | _acl.grantPermissionP(_who, _where, _what, params); 548 | } 549 | 550 | function _paramsTo256(uint8 _id,uint8 _op, uint240 _value) private returns (uint256) { 551 | return (uint256(_id) << 248) + (uint256(_op) << 240) + _value; 552 | } 553 | } 554 | -------------------------------------------------------------------------------- /contracts/Imports.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "@aragon/os/contracts/factory/ENSFactory.sol"; 4 | import "@aragon/os/contracts/factory/APMRegistryFactory.sol"; 5 | import "@aragon/os/contracts/factory/EVMScriptRegistryFactory.sol"; 6 | import "@aragon/id/contracts/FIFSResolvingRegistrar.sol"; 7 | import "@aragon/templates-shared/contracts/Migrations.sol"; 8 | 9 | contract Imports { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /contracts/external/IAragonFundraisingController.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | //import "@ablack/fundraising-batched-bancor-market-maker/contracts/BatchedBancorMarketMaker.sol"; 4 | //import "@ablack/fundraising-shared-interfaces/contracts/IPresale.sol"; 5 | //import "@ablack/fundraising-shared-interfaces/contracts/ITap.sol"; 6 | //import "@aragon/apps-vault/contracts/Vault.sol"; 7 | 8 | contract IAragonFundraisingController { 9 | 10 | bytes32 public constant UPDATE_FORMULA_ROLE = 0xbfb76d8d43f55efe58544ea32af187792a7bdb983850d8fed33478266eec3cbb; 11 | bytes32 public constant UPDATE_BENEFICIARY_ROLE = 0xf7ea2b80c7b6a2cab2c11d2290cb005c3748397358a25e17113658c83b732593; 12 | bytes32 public constant UPDATE_FEES_ROLE = 0x5f9be2932ed3a723f295a763be1804c7ebfd1a41c1348fb8bdf5be1c5cdca822; 13 | bytes32 public constant ADD_COLLATERAL_TOKEN_ROLE = 0x217b79cb2bc7760defc88529853ef81ab33ae5bb315408ce9f5af09c8776662d; 14 | bytes32 public constant REMOVE_COLLATERAL_TOKEN_ROLE = 0x2044e56de223845e4be7d0a6f4e9a29b635547f16413a6d1327c58d9db438ee2; 15 | bytes32 public constant UPDATE_COLLATERAL_TOKEN_ROLE = 0xe0565c2c43e0d841e206bb36a37f12f22584b4652ccee6f9e0c071b697a2e13d; 16 | bytes32 public constant OPEN_PRESALE_ROLE = 0xf323aa41eef4850a8ae7ebd047d4c89f01ce49c781f3308be67303db9cdd48c2; 17 | bytes32 public constant OPEN_TRADING_ROLE = 0x26ce034204208c0bbca4c8a793d17b99e546009b1dd31d3c1ef761f66372caf6; 18 | bytes32 public constant CONTRIBUTE_ROLE = 0x9ccaca4edf2127f20c425fdd86af1ba178b9e5bee280cd70d88ac5f6874c4f07; 19 | bytes32 public constant MAKE_BUY_ORDER_ROLE = 0x0dfea6908176d96adbee7026b3fe9fbdaccfc17bc443ddf14734fd27c3136179; 20 | bytes32 public constant MAKE_SELL_ORDER_ROLE = 0x52e3ace6a83e0c810920056ccc32fed5aa1e86287545113b03a52ab5c84e3f66; 21 | 22 | function initialize( 23 | address _presale, 24 | address _marketMaker, 25 | address _reserve 26 | ) external; 27 | 28 | function addCollateralToken( 29 | address _collateral, 30 | uint256 _virtualSupply, 31 | uint256 _virtualBalance, 32 | uint32 _reserveRatio 33 | ) external; 34 | } 35 | -------------------------------------------------------------------------------- /contracts/external/IBancorMarketMaker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract IBancorMarketMaker { 4 | 5 | bytes32 public constant CONTROLLER_ROLE = 0x7b765e0e932d348852a6f810bfa1ab891e259123f02db8cdcde614c570223357; 6 | 7 | function initialize( 8 | address _controller, 9 | address _tokenManager, 10 | address _formula, 11 | address _reserve, 12 | address _beneficiary, 13 | uint256 _buyFeePct, 14 | uint256 _sellFeePct 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /contracts/external/IConvictionVoting.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol"; 4 | import "@aragon/apps-vault/contracts/Vault.sol"; 5 | 6 | contract IConvictionVoting { 7 | 8 | bytes32 constant public CREATE_PROPOSALS_ROLE = keccak256("CREATE_PROPOSALS_ROLE"); 9 | 10 | function initialize(MiniMeToken _stakeToken, Vault _vault, address _requestToken) public; 11 | 12 | function initialize(MiniMeToken _stakeToken, Vault _vault, address _requestToken, uint256 _decay, uint256 _maxRatio); 13 | 14 | function initialize( 15 | MiniMeToken _stakeToken, 16 | Vault _vault, 17 | address _requestToken, 18 | uint256 _decay, 19 | uint256 _maxRatio, 20 | uint256 _weight 21 | ) public; 22 | } 23 | -------------------------------------------------------------------------------- /contracts/external/ITollgate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "@aragon/os/contracts/common/SafeERC20.sol"; 4 | import "@aragon/os/contracts/lib/token/ERC20.sol"; 5 | 6 | contract ITollgate { 7 | using SafeERC20 for ERC20; 8 | 9 | bytes32 public constant CHANGE_AMOUNT_ROLE = keccak256("CHANGE_AMOUNT_ROLE"); 10 | bytes32 public constant CHANGE_DESTINATION_ROLE = keccak256("CHANGE_DESTINATION_ROLE"); 11 | 12 | /** 13 | * @notice Initialize Tollgate with fee of `@tokenAmount(_feeToken, _feeAmount)` 14 | * @param _feeToken ERC20 address for the fee token 15 | * @param _feeAmount Amount of tokens collected as a fee on each forward 16 | * @param _feeDestination Destination for collected fees 17 | */ 18 | function initialize(ERC20 _feeToken, uint256 _feeAmount, address _feeDestination) external; 19 | } 20 | -------------------------------------------------------------------------------- /contracts/external/Token.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.4.24; 3 | 4 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 5 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; 6 | 7 | contract Token is ERC20, ERC20Detailed { 8 | 9 | constructor(address _owner, string name, string symbol) ERC20Detailed(name, symbol, 18) public { 10 | _mint(_owner, 10000000e18); 11 | } 12 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aragon/help-dao-template", 3 | "version": "1.0.0", 4 | "description": "Help Dao Template", 5 | "license": "GPL-3.0-or-later", 6 | "files": [ 7 | "abi/", 8 | "build/", 9 | "contracts/", 10 | "scripts/", 11 | "truffle.js", 12 | "test/" 13 | ], 14 | "scripts": { 15 | "prepublishOnly": "npm run compile && npm run abi:extract -- --no-compile", 16 | "abi:extract": "truffle-extract --output abi/ --keys abi", 17 | "compile": "truffle compile --all", 18 | "lint": "solium --dir ./contracts", 19 | "test": "npm run test:ganache", 20 | "test:ganache": "./node_modules/@aragon/templates-shared/scripts/test-ganache.sh", 21 | "test:geth": "npm run docker:run && npm run docker:wait-gas && npm run deploy:devnet && truffle test --network devnet && npm run docker:stop", 22 | "docker:run": "cd ./node_modules/@aragon/templates-shared/; docker-compose -f docker-compose.yml up -d; RESULT=$?; cd -; $(exit $RESULT)", 23 | "docker:stop": "cd ./node_modules/@aragon/templates-shared/; docker-compose down; cd -", 24 | "docker:wait-gas": "truffle exec ./node_modules/@aragon/templates-shared/scripts/sleep-until-gaslimit.js --network devnet 6900000", 25 | "deploy:rpc": "npm run compile && truffle exec scripts/deploy.js --network rpc", 26 | "deploy:devnet": "npm run compile && truffle exec scripts/deploy.js --network devnet", 27 | "deploy:aragen": "npm run compile && truffle exec scripts/deploy.js --network rpc --ens 0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1 --dao-factory 0x5d94e3e7aec542ab0f9129b9a7badeb5b3ca0f77 --mini-me-factory 0xd526b7aba39cccf76422835e7fd5327b98ad73c9 --register false", 28 | "deploy:staging": "npm run compile && truffle exec scripts/deploy.js --network rinkeby --ens 0xfe03625ea880a8cba336f9b5ad6e15b0a3b5a939 --dao-factory 0xf959e8953e2fe03782a7b86a7a5d948cb511ef5d --mini-me-factory 0xac443f983c38c02149ce2ffcff8b7de90dccf77c", 29 | "deploy:rinkeby": "npm run compile && truffle exec scripts/deploy.js --network rinkeby --ens 0x98Df287B6C145399Aaa709692c8D308357bC085D --dao-factory 0xad4d106b43b480faa3ef7f98464ffc27fc1faa96 --mini-me-factory 0x6ffeb4038f7f077c4d20eaf1706980caec31e2bf", 30 | "deploy:ropsten": "npm run compile && truffle exec scripts/deploy.js --network ropsten --ens 0x6afe2cacee211ea9179992f89dc61ff25c61e923 --dao-factory 0x233c587095d066bafe44b543a719c93ff16423f3 --mini-me-factory 0x1ce5621d386b2801f5600f1dbe29522805b8ac11", 31 | "deploy:mainnet": "npm run compile && truffle exec scripts/deploy.js --network mainnet --ens 0x314159265dd8dbb310642f98f50c066173c1259b --dao-factory 0xb9da44c051c6cc9e04b7e0f95e95d69c6a6d8031 --mini-me-factory 0x909d05f384d0663ed4be59863815ab43b4f347ec --gas-price 7", 32 | "publish:rpc": "aragon apm publish major $(npm run deploy:rpc | tail -n 1) --environment default", 33 | "publish:devnet": "aragon apm publish major $(npm run deploy:devnet | tail -n 1) --environment default", 34 | "publish:aragen": "aragon apm publish major $(npm run deploy:aragen | tail -n 1) --environment default", 35 | "publish:staging": "aragon apm publish major $(npm run deploy:staging | tail -n 1) --environment staging", 36 | "publish:rinkeby": "aragon apm publish major $(npm run deploy:rinkeby | tail -n 1) --environment rinkeby", 37 | "publish:ropsten": "aragon apm publish major $(npm run deploy:ropsten | tail -n 1) --environment ropsten", 38 | "publish:mainnet": "aragon apm publish major $(npm run deploy:mainnet | tail -n 1) --environment mainnet --gas-price 7" 39 | }, 40 | "dependencies": { 41 | "@aragon/os": "4.3.0", 42 | "@aragon/id": "2.0.3", 43 | "@aragon/apps-agent": "2.1.0", 44 | "@aragon/apps-vault": "^4.1.0", 45 | "@aragon/apps-voting": "^2.1.0", 46 | "@aragon/apps-finance": "^3.0.0", 47 | "@aragon/apps-token-manager": "^2.1.0", 48 | "@aragon/apps-shared-minime": "^1.0.0", 49 | "@aragon/templates-shared": "1.0.0", 50 | "@1hive/apps-dandelion-voting": "2.0.0", 51 | "@1hive/apps-redemptions": "2.0.1", 52 | "@1hive/apps-token-manager": "2.1.0", 53 | "@ablack/fundraising-aragon-fundraising": "^1.0.0", 54 | "@ablack/fundraising-bancor-formula": "^1.0.0", 55 | "@ablack/fundraising-batched-bancor-market-maker": "^1.0.0", 56 | "@ablack/fundraising-presale": "^1.0.0", 57 | "@ablack/fundraising-tap": "^1.0.0" 58 | }, 59 | "devDependencies": { 60 | "@aragon/test-helpers": "^2.0.0", 61 | "@aragon/cli": "7.0.3", 62 | "eth-ens-namehash": "^2.0.8", 63 | "eth-gas-reporter": "^0.1.1", 64 | "ganache-cli": "^6.0.3", 65 | "solium": "^1.0.4", 66 | "truffle": "4.1.14", 67 | "truffle-extract": "^1.2.1", 68 | "truffle-hdwallet-provider": "0.0.3", 69 | "openzeppelin-solidity": "2.0.1" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | const deployTemplate = require('@aragon/templates-shared/scripts/deploy-template') 2 | 3 | const TEMPLATE_NAME = 'gardens-template' 4 | const CONTRACT_NAME = 'GardensTemplate' 5 | 6 | module.exports = (callback) => { 7 | deployTemplate(web3, artifacts, TEMPLATE_NAME, CONTRACT_NAME) 8 | .then(template => { 9 | console.log("Gardens Template address: ", template.address) 10 | }) 11 | .catch(error => console.log(error)) 12 | .finally(callback) 13 | } 14 | -------------------------------------------------------------------------------- /scripts/new-dao.js: -------------------------------------------------------------------------------- 1 | const GardensTemplate = artifacts.require("GardensTemplate") 2 | const MiniMeToken = artifacts.require("MiniMeToken") 3 | const Token = artifacts.require("Token") 4 | 5 | const DAO_ID = "gardens" + Math.random() // Note this must be unique for each deployment, change it for subsequent deployments 6 | const TOKEN_OWNER = "0xb4124cEB3451635DAcedd11767f004d8a28c6eE7" 7 | const NETWORK_ARG = "--network" 8 | const DAO_ID_ARG = "--daoid" 9 | const NON_MINIME_COLLATERAL = "--nonminime" 10 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' 11 | 12 | const argValue = (arg, defaultValue) => process.argv.includes(arg) ? process.argv[process.argv.indexOf(arg) + 1] : defaultValue 13 | 14 | const network = () => argValue(NETWORK_ARG, "local") 15 | const daoId = () => argValue(DAO_ID_ARG, DAO_ID) 16 | const nonMiniMeCollateral = () => argValue(NON_MINIME_COLLATERAL, false) 17 | 18 | const gardensTemplateAddress = () => { 19 | if (network() === "rinkeby") { 20 | const Arapp = require("../arapp") 21 | return Arapp.environments.rinkeby.address 22 | } else if (network() === "mainnet") { 23 | const Arapp = require("../arapp") 24 | return Arapp.environments.mainnet.address 25 | } else { 26 | const Arapp = require("../arapp_local") 27 | return Arapp.environments.devnet.address 28 | } 29 | } 30 | 31 | const DAYS = 24 * 60 * 60 32 | const ONE_HUNDRED_PERCENT = 1e18 33 | const ONE_TOKEN = 1e18 34 | const FUNDRAISING_ONE_HUNDRED_PERCENT = 1e6 35 | const FUNDRAISING_ONE_TOKEN = 1e6 36 | 37 | const COLLATERAL_TOKEN_NAME = "Honey" 38 | const COLLATERAL_TOKEN_SYMBOL = "HNY" 39 | const COLLATERAL_TOKEN_DECIMALS = 18 40 | const COLLATERAL_TOKEN_TRANSFERS_ENABLED = true 41 | const COLLATERAL_BALANCE = 10e23 42 | 43 | // Create dao transaction one config 44 | const ORG_TOKEN_NAME = "OrgToken" 45 | const ORG_TOKEN_SYMBOL = "OGT" 46 | const SUPPORT_REQUIRED = 0.5 * ONE_HUNDRED_PERCENT 47 | const MIN_ACCEPTANCE_QUORUM = 0.2 * ONE_HUNDRED_PERCENT 48 | const VOTE_DURATION_BLOCKS = 15 49 | const VOTE_BUFFER_BLOCKS = 10 50 | const VOTE_EXECUTION_DELAY_BLOCKS = 5 51 | const VOTING_SETTINGS = [SUPPORT_REQUIRED, MIN_ACCEPTANCE_QUORUM, VOTE_DURATION_BLOCKS, VOTE_BUFFER_BLOCKS, VOTE_EXECUTION_DELAY_BLOCKS] 52 | const USE_AGENT_AS_VAULT = false 53 | 54 | // Create dao transaction two config 55 | const TOLLGATE_FEE = ONE_TOKEN 56 | const USE_CONVICTION_AS_FINANCE = true 57 | const FINANCE_PERIOD = 0 // Irrelevant if using conviction as finance 58 | 59 | // Create dao transaction three config 60 | const PRESALE_GOAL = 1000 * ONE_TOKEN 61 | const PRESALE_PERIOD = 14 * DAYS 62 | const PRESALE_EXCHANGE_RATE = FUNDRAISING_ONE_TOKEN 63 | const VESTING_CLIFF_PERIOD = 90 * DAYS 64 | const VESTING_COMPLETE_PERIOD = 360 * DAYS 65 | const PRESALE_PERCENT_SUPPLY_OFFERED = FUNDRAISING_ONE_HUNDRED_PERCENT 66 | const PRESALE_PERCENT_FUNDING_FOR_BENEFICIARY = 0.5 * FUNDRAISING_ONE_HUNDRED_PERCENT 67 | const OPEN_DATE = 0 68 | const BUY_FEE_PCT = 0.2 * ONE_HUNDRED_PERCENT 69 | const SELL_FEE_PCT = 0.2 * ONE_HUNDRED_PERCENT 70 | 71 | // Create dao transaction four config 72 | const VIRTUAL_SUPPLY = 2 73 | const VIRTUAL_BALANCE = 1 74 | const RESERVE_RATIO = 0.1 * FUNDRAISING_ONE_HUNDRED_PERCENT 75 | 76 | module.exports = async (callback) => { 77 | try { 78 | const gardensTemplate = await GardensTemplate.at(gardensTemplateAddress()) 79 | let collateralToken 80 | 81 | if (nonMiniMeCollateral()) { 82 | collateralToken = await Token.new(TOKEN_OWNER, COLLATERAL_TOKEN_NAME, COLLATERAL_TOKEN_SYMBOL) 83 | } else { 84 | collateralToken = await MiniMeToken.new( 85 | ZERO_ADDRESS, 86 | ZERO_ADDRESS, 87 | 0, 88 | COLLATERAL_TOKEN_NAME, 89 | COLLATERAL_TOKEN_DECIMALS, 90 | COLLATERAL_TOKEN_SYMBOL, 91 | COLLATERAL_TOKEN_TRANSFERS_ENABLED 92 | ) 93 | collateralToken.generateTokens(TOKEN_OWNER, COLLATERAL_BALANCE) 94 | } 95 | console.log(`Created ${COLLATERAL_TOKEN_SYMBOL} Token: ${collateralToken.address}`) 96 | 97 | const createDaoTxOneReceipt = await gardensTemplate.createDaoTxOne( 98 | ORG_TOKEN_NAME, 99 | ORG_TOKEN_SYMBOL, 100 | VOTING_SETTINGS, 101 | USE_AGENT_AS_VAULT 102 | ); 103 | console.log(`Tx One Complete. DAO address: ${createDaoTxOneReceipt.logs.find(x => x.event === "DeployDao").args.dao} Gas used: ${createDaoTxOneReceipt.receipt.gasUsed} `) 104 | 105 | const createDaoTxTwoReceipt = await gardensTemplate.createDaoTxTwo( 106 | collateralToken.address, 107 | TOLLGATE_FEE, 108 | [collateralToken.address], 109 | USE_CONVICTION_AS_FINANCE, 110 | FINANCE_PERIOD, 111 | collateralToken.address 112 | ) 113 | console.log(`Tx Two Complete. Gas used: ${createDaoTxTwoReceipt.receipt.gasUsed}`) 114 | 115 | const createDaoTxThreeReceipt = await gardensTemplate.createDaoTxThree( 116 | PRESALE_GOAL, 117 | PRESALE_PERIOD, 118 | PRESALE_EXCHANGE_RATE, 119 | VESTING_CLIFF_PERIOD, 120 | VESTING_COMPLETE_PERIOD, 121 | PRESALE_PERCENT_SUPPLY_OFFERED, 122 | PRESALE_PERCENT_FUNDING_FOR_BENEFICIARY, 123 | OPEN_DATE, 124 | BUY_FEE_PCT, 125 | SELL_FEE_PCT 126 | ) 127 | console.log(`Tx Three Complete. Gas used: ${createDaoTxThreeReceipt.receipt.gasUsed}`) 128 | 129 | const createDaoTxFourReceipt = await gardensTemplate.createDaoTxFour( 130 | daoId(), 131 | VIRTUAL_SUPPLY, 132 | VIRTUAL_BALANCE, 133 | RESERVE_RATIO 134 | ) 135 | console.log(`Tx Four Complete. Gas used: ${createDaoTxFourReceipt.receipt.gasUsed}`) 136 | 137 | } catch (error) { 138 | console.log(error) 139 | } 140 | callback() 141 | } 142 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | const homedir = require('homedir') 2 | const path = require('path') 3 | 4 | const HDWalletProvider = require('truffle-hdwallet-provider') 5 | const HDWalletProviderPrivkey = require('truffle-hdwallet-provider-privkey') 6 | 7 | const DEFAULT_MNEMONIC = 'stumble story behind hurt patient ball whisper art swift tongue ice alien' 8 | 9 | const defaultRPC = (network) => 10 | `https://${network}.infura.io` 11 | 12 | const configFilePath = (filename) => 13 | path.join(homedir(), `.aragon/${filename}`) 14 | 15 | const mnemonic = () => { 16 | try { 17 | return require(configFilePath('mnemonic.json')).mnemonic 18 | } catch (e) { 19 | return DEFAULT_MNEMONIC 20 | } 21 | } 22 | 23 | const settingsForNetwork = (network) => { 24 | try { 25 | return require(configFilePath(`${network}_key.json`)) 26 | } catch (e) { 27 | return { } 28 | } 29 | } 30 | 31 | // Lazily loaded provider 32 | const providerForNetwork = (network) => ( 33 | () => { 34 | let { rpc, keys } = settingsForNetwork(network) 35 | 36 | rpc = rpc || defaultRPC(network) 37 | 38 | if (!keys || keys.length == 0) { 39 | return new HDWalletProvider(mnemonic(), rpc) 40 | } 41 | 42 | return new HDWalletProviderPrivkey(keys, rpc) 43 | } 44 | ) 45 | 46 | const mochaGasSettings = { 47 | reporter: 'eth-gas-reporter', 48 | reporterOptions : { 49 | currency: 'USD', 50 | gasPrice: 3 51 | } 52 | } 53 | 54 | const mocha = process.env.GAS_REPORTER ? mochaGasSettings : {} 55 | 56 | module.exports = { 57 | networks: { 58 | rpc: { 59 | network_id: 15, 60 | host: 'localhost', 61 | port: 8545, 62 | gas: 8.9e6, 63 | gasPrice: 20000000001 64 | }, 65 | devnet: { 66 | network_id: 16, 67 | host: 'localhost', 68 | port: 8555, 69 | gas: 8.9e6, 70 | gasPrice: 15000000001 71 | }, 72 | mainnet: { 73 | network_id: 1, 74 | provider: providerForNetwork('mainnet'), 75 | gas: 7.9e6, 76 | gasPrice: 8000000001 77 | }, 78 | ropsten: { 79 | network_id: 3, 80 | provider: providerForNetwork('ropsten'), 81 | gas: 4.712e6 82 | }, 83 | rinkeby: { 84 | network_id: 4, 85 | provider: providerForNetwork('rinkeby'), 86 | gas: 7.9e6, 87 | gasPrice: 15000000001 88 | }, 89 | kovan: { 90 | network_id: 42, 91 | provider: providerForNetwork('kovan'), 92 | gas: 6.9e6 93 | }, 94 | coverage: { 95 | host: "localhost", 96 | network_id: "*", 97 | port: 8555, 98 | gas: 0xffffffffff, 99 | gasPrice: 0x01 100 | }, 101 | }, 102 | build: {}, 103 | mocha, 104 | solc: { 105 | optimizer: { 106 | enabled: true, 107 | runs: 1 108 | } 109 | }, 110 | } 111 | --------------------------------------------------------------------------------