From 482f7d85bef10436520210972a03894bc42c5ac7 Mon Sep 17 00:00:00 2001 From: Jonathan Schwartz Date: Wed, 4 Dec 2019 07:41:17 -0500 Subject: [PATCH] add storage to oe template (not working) --- apps/storage/.eslintignore | 6 + apps/storage/.eslintrc.json | 36 +++++ apps/storage/.gitattributes | 1 + apps/storage/.gitignore | 9 ++ apps/storage/.ipfsignore | 15 +++ apps/storage/.solcover.js | 7 + apps/storage/.soliumignore | 2 + apps/storage/.soliumrc.json | 35 +++++ apps/storage/README.md | 125 ++++++++++++++++++ apps/storage/arapp.json | 35 +++++ apps/storage/contracts/Storage.sol | 38 ++++++ apps/storage/contracts/Template.sol | 104 +++++++++++++++ apps/storage/contracts/misc/TestImports.sol | 16 +++ apps/storage/manifest.json | 9 ++ apps/storage/package.json | 64 +++++++++ apps/storage/public/meta/details.md | 1 + apps/storage/public/meta/icon.svg | 33 +++++ apps/storage/test/app.js | 86 ++++++++++++ apps/storage/truffle-config.js | 1 + templates/dev/contracts/DevTemplate.sol | 6 + .../open-enterprise/contracts/BaseOEApps.sol | 11 ++ 21 files changed, 640 insertions(+) create mode 100644 apps/storage/.eslintignore create mode 100644 apps/storage/.eslintrc.json create mode 100644 apps/storage/.gitattributes create mode 100644 apps/storage/.gitignore create mode 100644 apps/storage/.ipfsignore create mode 100644 apps/storage/.solcover.js create mode 100644 apps/storage/.soliumignore create mode 100644 apps/storage/.soliumrc.json create mode 100644 apps/storage/README.md create mode 100644 apps/storage/arapp.json create mode 100644 apps/storage/contracts/Storage.sol create mode 100644 apps/storage/contracts/Template.sol create mode 100644 apps/storage/contracts/misc/TestImports.sol create mode 100644 apps/storage/manifest.json create mode 100644 apps/storage/package.json create mode 100644 apps/storage/public/meta/details.md create mode 100644 apps/storage/public/meta/icon.svg create mode 100644 apps/storage/test/app.js create mode 100644 apps/storage/truffle-config.js diff --git a/apps/storage/.eslintignore b/apps/storage/.eslintignore new file mode 100644 index 000000000..5d204aa21 --- /dev/null +++ b/apps/storage/.eslintignore @@ -0,0 +1,6 @@ +node_modules +app/node_modules +build +.cache +dist +coverage diff --git a/apps/storage/.eslintrc.json b/apps/storage/.eslintrc.json new file mode 100644 index 000000000..b9d24e7d8 --- /dev/null +++ b/apps/storage/.eslintrc.json @@ -0,0 +1,36 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": [ + "standard", + "standard-react", + "prettier", + "prettier/react" + ], + "parser": "babel-eslint", + "parserOptions": { + "ecmaVersion": 6, + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "module" + }, + "plugins": ["prettier", "react"], + "rules": { + "valid-jsdoc": "error", + "react/prop-types": 0, + "prettier/prettier": [ + "error", + { + "singleQuote": true, + "semi": false, + "trailingComma": "es5", + "bracketSpacing": true, + "jsxBracketSameLine": false + } + ], + "linebreak-style": ["error", "unix"] + } +} diff --git a/apps/storage/.gitattributes b/apps/storage/.gitattributes new file mode 100644 index 000000000..7cc88f065 --- /dev/null +++ b/apps/storage/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity \ No newline at end of file diff --git a/apps/storage/.gitignore b/apps/storage/.gitignore new file mode 100644 index 000000000..8e5576519 --- /dev/null +++ b/apps/storage/.gitignore @@ -0,0 +1,9 @@ +node_modules +build +abi +.cache +dist +ipfs.cmd +package-lock.json +coverage.json +coverage diff --git a/apps/storage/.ipfsignore b/apps/storage/.ipfsignore new file mode 100644 index 000000000..490d689a4 --- /dev/null +++ b/apps/storage/.ipfsignore @@ -0,0 +1,15 @@ +# Git files +.gitignore + +# Build files +.cache +node_modules +build +coverage + +# Lock files +package-lock.json +yarn.lock + +# Others +test diff --git a/apps/storage/.solcover.js b/apps/storage/.solcover.js new file mode 100644 index 000000000..954905775 --- /dev/null +++ b/apps/storage/.solcover.js @@ -0,0 +1,7 @@ +module.exports = { + norpc: true, + copyPackages: [], + skipFiles: [ + 'test', + ] +} diff --git a/apps/storage/.soliumignore b/apps/storage/.soliumignore new file mode 100644 index 000000000..15c47afcc --- /dev/null +++ b/apps/storage/.soliumignore @@ -0,0 +1,2 @@ +node_modules +contracts/Migrations.sol diff --git a/apps/storage/.soliumrc.json b/apps/storage/.soliumrc.json new file mode 100644 index 000000000..dcb7531d4 --- /dev/null +++ b/apps/storage/.soliumrc.json @@ -0,0 +1,35 @@ +{ + "extends": "solium:all", + "plugins": [ + "security" + ], + "rules": { + "imports-on-top": ["error"], + "variable-declarations": ["error"], + "array-declarations": ["error"], + "operator-whitespace": ["error"], + "lbrace": ["error"], + "mixedcase": ["warning"], + "camelcase": ["error"], + "uppercase": ["warning"], + "no-empty-blocks": ["error"], + "no-unused-vars": ["error"], + "quotes": ["error"], + "indentation": ["error"], + "whitespace": ["error"], + "deprecated-suicide": ["error"], + "arg-overflow": ["error", 8], + "pragma-on-top": ["error"], + "security/enforce-explicit-visibility": ["error"], + "consequent": 0, + "error-reason": ["warning"], + "function-order": [ + "error", + { + "ignore": { + "functions": ["initialize"] + } + } + ] + } +} diff --git a/apps/storage/README.md b/apps/storage/README.md new file mode 100644 index 000000000..3805b43cb --- /dev/null +++ b/apps/storage/README.md @@ -0,0 +1,125 @@ +# Aragon React Boilerplate + +> 🕵️ [Find more boilerplates using GitHub](https://github.com/search?q=topic:aragon-boilerplate) | +> ✨ [Official boilerplates](https://github.com/search?q=topic:aragon-boilerplate+org:aragon) + +React boilerplate for Aragon applications. + +This boilerplate includes a fully working example app, complete with a background worker and a front-end in React (with Aragon UI). Also comes with a DAO Template which will allow for using your app to interact with other Aragon apps like the Voting app. You can read more about DAO Template [here](https://hack.aragon.org/docs/templates-intro). + +## Usage + +To setup use the command `create-aragon-app`: + +```sh +npx create-aragon-app react +``` + +## Structure + +This boilerplate has the following structure: + +```md +root +├── app +├ ├── src +├ └── package.json +├── contracts +├ ├── CounterApp.sol +├ └── Template.sol +├── migration +├── test +├── arapp.json +├── manifest.json +├── truffle.js +└── package.json +``` + +- **app**: Frontend folder. Completely encapsulated, has its own package.json and dependencies. + - **src**: Source files. + - [**package.json**](https://docs.npmjs.com/creating-a-package-json-file): Frontend npm configuration file. +- **contracts**: Smart Constracts folder. + - `CounterApp.sol`: Aragon app contract example. + - `Template.sol`: [Aragon Template](https://hack.aragon.org/docs/templates-intro) to deploy a fully functional DAO. +- [**migrations**](https://truffleframework.com/docs/truffle/getting-started/running-migrations): Migrations folder. +- **test**: Tests folder. +- [**arapp.json**](https://hack.aragon.org/docs/cli-global-confg#the-arappjson-file): Aragon configuration file. Includes Aragon-specific metadata for your app. +- [**manifest.json**](https://hack.aragon.org/docs/cli-global-confg#the-manifestjson-file): Aragon configuration file. Includes web-specific configurations. +- [**truffle.js**](https://truffleframework.com/docs/truffle/reference/configuration): Truffle configuration file. +- [**package.json**](https://docs.npmjs.com/creating-a-package-json-file): Main npm configuration file. + +## Make the template work with your app + +- Edit the roles defined in the template to configure your DAO as you want! + +## Run the template + +```sh +npx aragon run --template Template --template-init @ARAGON_ENS +``` + +## Running your app + +### Using HTTP + +Running your app using HTTP will allow for a faster development process of your app's front-end, as it can be hot-reloaded without the need to execute `aragon run` every time a change is made. + +- First start your app's development server running `npm run start:app`, and keep that process running. By default it will rebuild the app and reload the server when changes to the source are made. + +- After that, you can run `npm run start:http` or `npm run start:http:template` which will compile your app's contracts, publish the app locally and create a DAO. You will need to stop it and run it again after making changes to your smart contracts. + +Changes to the app's background script (`app/script.js`) cannot be hot-reloaded, after making changes to the script, you will need to either restart the development server (`npm run start:app`) or rebuild the script `npm run build:script`. + +### Using IPFS + +Running your app using IPFS will mimic the production environment that will be used for running your app. `npm run start:ipfs` will run your app using IPFS. Whenever a change is made to any file in your front-end, a new version of the app needs to be published, so the command needs to be restarted. + +## What's in this boilerplate? + +### npm Scripts + +- **start** or **start:ipfs**: Runs your app inside a DAO served from IPFS +- **start:http**: Runs your app inside a DAO served with HTTP (hot reloading) +- **start:ipfs:template**: Creates a DAO with the [Template](https://github.com/aragon/aragon-react-boilerplate/blob/master/contracts/Template.sol) and serves the app from IPFS +- **start:http:template**: Creates a DAO with the [Template](https://github.com/aragon/aragon-react-boilerplate/blob/master/contracts/Template.sol) and serves the app with HTTP (hot reloading) +- **prepare**: Installs dependencies of the front-end +- **start:app**: Starts a development server for your app +- **compile**: Compiles the smart contracts +- **build**: Builds the front-end and background script +- **test**: Runs tests for the contracts +- **publish:patch**: Releases a patch version to aragonPM (only frontend/content changes allowed) +- **publish:minor**: Releases a minor version to aragonPM (only frontend/content changes allowed) +- **publish:major**: Releases a major version to aragonPM (frontend **and** contract changes) +- **versions**: Checks the currently installed versions of the app +- **lint**: Checks the app and the contracts for linting errors +- **lint:fix**: Fixes the lint errors that can be resolved automatically +- **coverage**: Runs the tests for the contracts and creates a report + +### Libraries + +- [**@aragon/os**](https://github.com/aragon/aragonos): Aragon interfaces +- [**@aragon/api**](https://github.com/aragon/aragon.js/tree/master/packages/aragon-api): Wrapper for Aragon application RPC +- [**@aragon/ui**](https://github.com/aragon/aragon-ui): Aragon UI components (in React) + +## What you can do with this boilerplate? + +### Publish + +You can publish you app on [aragonPM](https://hack.aragon.org/docs/apm). See how in our [publish guide](https://hack.aragon.org/docs/guides-publish). + +> **Note**
+> The [Template](https://github.com/aragon/aragon-react-boilerplate/blob/master/contracts/Template.sol) will not be published. + +### Using a different Ethereum account + +You can use a different account to interact with you app. [Check the documentation](https://hack.aragon.org/docs/guides-faq#set-a-private-key). + +### Propagate content + +You can propagate the content of your app on IPFS. Learn more in our [troubleshooting guide](https://hack.aragon.org/docs/guides-faq#propagating-your-content-hash-through-ipfs) or use the `aragon ipfs propagate` command: + +``` +npx aragon ipfs propagate +``` + +Where `cid` is your content id hash (this will be displayed after publishing). diff --git a/apps/storage/arapp.json b/apps/storage/arapp.json new file mode 100644 index 000000000..907632b05 --- /dev/null +++ b/apps/storage/arapp.json @@ -0,0 +1,35 @@ +{ + "roles": [ + { + "name": "Register a key-value pair", + "id": "REGISTER_DATA_ROLE", + "params": [] + }, + { + "name": "Register a storage provider", + "id": "REGISTER_STORAGE_PROVIDER_ROLE", + "params": [] + } + ], + "environments": { + "default": { + "registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1", + "network": "rpc", + "appName": "storage.aragonpm.eth" + }, + "rinkeby": { + "registry": "0x98df287b6c145399aaa709692c8d308357bc085d", + "appName": "storage.open.aragonpm.eth", + "wsRPC": "wss://rinkeby.eth.aragon.network/ws", + "network": "rinkeby" + }, + "mainnet": { + "registry": "0x314159265dd8dbb310642f98f50c066173c1259b", + "appName": "storage.open.aragonpm.eth", + "wsRPC": "wss://mainnet.eth.aragon.network/ws", + "network": "mainnet" + } + }, + "path": "contracts/Storage.sol", + "storage": true +} diff --git a/apps/storage/contracts/Storage.sol b/apps/storage/contracts/Storage.sol new file mode 100644 index 000000000..62dba0dd6 --- /dev/null +++ b/apps/storage/contracts/Storage.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.4.24; + +import "@aragon/os/contracts/apps/AragonApp.sol"; + + +contract Storage is AragonApp { + + /// Events + event Registered(bytes32 indexed key); + event PinHash(string cid); + + /// State: data registry + mapping(bytes32 => string) internal registeredData; + + /// ACL + bytes32 constant public REGISTER_DATA_ROLE = keccak256("REGISTER_DATA_ROLE"); + + /// Custom aragon constructor + function initialize() public onlyInit { + initialized(); + } + + /** + * @notice Set `_key` data to `_cid` + * @param _key Data item that will be stored in the registry + * @param _cid Data content to be stored + */ + + function registerData(bytes32 _key, string _cid) external auth(REGISTER_DATA_ROLE) { + registeredData[_key] = _cid; + emit Registered(_key); + emit PinHash(_cid); + } + + function getRegisteredData(bytes32 _key) external view returns(string) { + return registeredData[_key]; + } +} diff --git a/apps/storage/contracts/Template.sol b/apps/storage/contracts/Template.sol new file mode 100644 index 000000000..12f6f5c4e --- /dev/null +++ b/apps/storage/contracts/Template.sol @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identitifer: GPL-3.0-or-later + * + * This file requires contract dependencies which are licensed as + * GPL-3.0-or-later, forcing it to also be licensed as such. + * + * This is the only file in your project that requires this license and + * you are free to choose a different license for the rest of the project. + */ + +pragma solidity ^0.4.24; + +import "@aragon/os/contracts/factory/DAOFactory.sol"; +import "@aragon/os/contracts/apm/Repo.sol"; +import "@aragon/os/contracts/lib/ens/ENS.sol"; +import "@aragon/os/contracts/lib/ens/PublicResolver.sol"; +import "@aragon/os/contracts/apm/APMNamehash.sol"; + +import "@aragon/apps-voting/contracts/Voting.sol"; +import "@aragon/apps-token-manager/contracts/TokenManager.sol"; +import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol"; + +import "./Storage.sol"; + + +contract TemplateBase is APMNamehash { + ENS public ens; + DAOFactory public fac; + + event DeployInstance(address dao); + event InstalledApp(address appProxy, bytes32 appId); + + constructor(DAOFactory _fac, ENS _ens) public { + ens = _ens; + + // If no factory is passed, get it from on-chain bare-kit + if (address(_fac) == address(0)) { + bytes32 bareKit = apmNamehash("bare-kit"); + fac = TemplateBase(latestVersionAppBase(bareKit)).fac(); + } else { + fac = _fac; + } + } + + function latestVersionAppBase(bytes32 appId) public view returns (address base) { + Repo repo = Repo(PublicResolver(ens.resolver(appId)).addr(appId)); + (,base,) = repo.getLatest(); + + return base; + } +} + + +contract Template is TemplateBase { + MiniMeTokenFactory tokenFactory; + + uint64 constant PCT = 10 ** 16; + address constant ANY_ENTITY = address(-1); + + constructor(ENS ens) TemplateBase(DAOFactory(0), ens) public { + tokenFactory = new MiniMeTokenFactory(); + } + + function newInstance() public { + Kernel dao = fac.newDAO(this); + ACL acl = ACL(dao.acl()); + acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this); + + address root = msg.sender; + bytes32 appId = apmNamehash("storage"); + bytes32 votingAppId = apmNamehash("voting"); + bytes32 tokenManagerAppId = apmNamehash("token-manager"); + + Storage app = Storage(dao.newAppInstance(appId, latestVersionAppBase(appId), new bytes(0), true)); + Voting voting = Voting(dao.newAppInstance(votingAppId, latestVersionAppBase(votingAppId))); + TokenManager tokenManager = TokenManager(dao.newAppInstance(tokenManagerAppId, latestVersionAppBase(tokenManagerAppId))); + + MiniMeToken token = tokenFactory.createCloneToken(MiniMeToken(0), 0, "App token", 0, "APP", true); + token.changeController(tokenManager); + + app.initialize(); + tokenManager.initialize(token, true, 0); + // Initialize apps + voting.initialize(token, 50 * PCT, 20 * PCT, 1 days); + + acl.createPermission(this, tokenManager, tokenManager.MINT_ROLE(), this); + tokenManager.mint(root, 1); // Give one token to root + + acl.createPermission(ANY_ENTITY, voting, voting.CREATE_VOTES_ROLE(), root); + + // TODO: Handle REGISTER_DATA_ROLE from voting instead of allowing everyone. + acl.createPermission(ANY_ENTITY, app, app.REGISTER_DATA_ROLE(), root); + // Clean up permissions + acl.grantPermission(root, dao, dao.APP_MANAGER_ROLE()); + acl.revokePermission(this, dao, dao.APP_MANAGER_ROLE()); + acl.setPermissionManager(root, dao, dao.APP_MANAGER_ROLE()); + + acl.grantPermission(root, acl, acl.CREATE_PERMISSIONS_ROLE()); + acl.revokePermission(this, acl, acl.CREATE_PERMISSIONS_ROLE()); + acl.setPermissionManager(root, acl, acl.CREATE_PERMISSIONS_ROLE()); + + emit DeployInstance(dao); + } +} diff --git a/apps/storage/contracts/misc/TestImports.sol b/apps/storage/contracts/misc/TestImports.sol new file mode 100644 index 000000000..a07a1610d --- /dev/null +++ b/apps/storage/contracts/misc/TestImports.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.4.24; + +// import "@tps/test-helpers/contracts/acl/ACL.sol"; +// import "@tps/test-helpers/contracts/kernel/Kernel.sol"; +import "@aragon/os/contracts/factory/DAOFactory.sol"; +// import "@tps/test-helpers/contracts/factory/EVMScriptRegistryFactory.sol"; + +import "@aragon/apps-shared-migrations/contracts/Migrations.sol"; +// import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol"; +// import "@tps/test-helpers/contracts/lib/bounties/StandardBounties.sol"; + + +contract TestImports { + constructor() public { // solium-disable-line + } +} \ No newline at end of file diff --git a/apps/storage/manifest.json b/apps/storage/manifest.json new file mode 100644 index 000000000..6c2e51056 --- /dev/null +++ b/apps/storage/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "Storage", + "author": "Autark", + "description": "Store an organization's wide settings.", + "changelog_url": "https://github.com/AutarkLabs/aragon-storage/releases", + "details_url": "/meta/details.md", + "source_url": "https://", + "icons": [{ "src": "/meta/icon.svg", "sizes": "56x56" }] +} diff --git a/apps/storage/package.json b/apps/storage/package.json new file mode 100644 index 000000000..0368a7afd --- /dev/null +++ b/apps/storage/package.json @@ -0,0 +1,64 @@ +{ + "name": "aragon-storage", + "version": "0.0.1", + "scripts": { + "dev": "npm run sync-assets && parcel app/index.html --port 9999", + "copy-public-assets": "rsync -rtu ./public/ ./dist", + "sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist && npm run copy-public-assets", + "devchain:reset": "aragon devchain --reset", + "clean:start": "aragon clean && rm -rf ~/.aragon && npm run start:http:template", + "start": "npm run start:ipfs", + "start:ipfs": "aragon run", + "start:http": "aragon run --http localhost:8001 --http-served-from ./dist", + "start:ipfs:template": "npm run start:ipfs -- --template Template --template-init @ARAGON_ENS", + "start:http:template": "rm -rf build && npm run start:http -- --template Template --template-init @ARAGON_ENS", + "start:app": "cd app && npm start && cd ..", + "test": "cross-env TRUFFLE_TEST=true npm run ganache-cli:test", + "compile": "aragon contracts compile", + "build": "npm run compile && npm run sync-assets && npm run build:app && npm run build:script", + "build:app": "parcel build ./app/index.html -d dist/ --public-url \".\" --no-cache", + "build:script": "parcel build ./app/script.js -d dist/ --no-cache", + "publish:cd": "yes | aragon apm publish major --files dist/ --environment continuous-deployment --apm.ipfs.rpc https://ipfs.autark.xyz:5001 --ipfs-check false --skip-confirmation true", + "publish:http": "npm run build:script && yes | aragon apm publish major --files dist --http localhost:9999 --http-served-from ./dist --propagate-content false --skip-confirmation true", + "publish:patch": "aragon apm publish patch", + "publish:minor": "aragon apm publish minor", + "publish:major": "aragon apm publish major", + "versions": "aragon apm versions", + "lint": "solium --dir ./contracts", + "lint:fix": "eslint . --fix && solium --dir ./contracts --fix", + "coverage": "cross-env SOLIDITY_COVERAGE=true npm run ganache-cli:test", + "ganache-cli:test": "sh ./node_modules/@aragon/test-helpers/ganache-cli.sh", + "frontend": "npm run sync-assets --port 8888" + }, + "keywords": [], + "files": [ + "/abi", + "/arapp.json", + "/build", + "/contracts", + "/test" + ], + "author": "Autark ", + "contributors": [], + "license": "GPL-3.0-or-later", + "description": "", + "devDependencies": { + "@aragon/apps-shared-migrations": "1.0.0", + "@aragon/apps-shared-minime": "^1.0.0", + "@aragon/apps-token-manager": "2.0.0", + "@aragon/apps-voting": "2.0.0", + "@aragon/cli": "^6.3.3", + "@aragon/test-helpers": "^1.1.0", + "eth-ens-namehash": "^2.0.8", + "eth-gas-reporter": "^0.2.0", + "ethereumjs-testrpc-sc": "^6.1.6", + "ganache-cli": "^6.4.3", + "solidity-coverage": "^0.5.11", + "solium": "^1.2.3", + "truffle": "4.1.14", + "truffle-extract": "^1.2.1" + }, + "dependencies": { + "@aragon/os": "^4.2.0" + } +} diff --git a/apps/storage/public/meta/details.md b/apps/storage/public/meta/details.md new file mode 100644 index 000000000..94827a4b9 --- /dev/null +++ b/apps/storage/public/meta/details.md @@ -0,0 +1 @@ +Storage App diff --git a/apps/storage/public/meta/icon.svg b/apps/storage/public/meta/icon.svg new file mode 100644 index 000000000..b908822bf --- /dev/null +++ b/apps/storage/public/meta/icon.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/storage/test/app.js b/apps/storage/test/app.js new file mode 100644 index 000000000..8b6364c83 --- /dev/null +++ b/apps/storage/test/app.js @@ -0,0 +1,86 @@ +/* global artifacts contract before beforeEach it assert */ +const { assertRevert } = require('@aragon/test-helpers/assertThrow') + +const Storage = artifacts.require('Storage.sol') +const DAOFactory = artifacts.require( + '@aragon/os/contracts/factory/DAOFactory' +) +const EVMScriptRegistryFactory = artifacts.require( + '@aragon/os/contracts/factory/EVMScriptRegistryFactory' +) +const ACL = artifacts.require('@aragon/os/contracts/acl/ACL') +const Kernel = artifacts.require('@aragon/os/contracts/kernel/Kernel') + +const getContract = name => artifacts.require(name) + +const ANY_ADDRESS = '0xffffffffffffffffffffffffffffffffffffffff' + +contract('Storage', accounts => { + let APP_MANAGER_ROLE, REGISTER_DATA_ROLE + let daoFact, appBase, app + + const firstAccount = accounts[0] + const secondAccount = accounts[1] + + before(async () => { + const kernelBase = await getContract('Kernel').new(true) // petrify immediately + const aclBase = await getContract('ACL').new() + const regFact = await EVMScriptRegistryFactory.new() + daoFact = await DAOFactory.new( + kernelBase.address, + aclBase.address, + regFact.address + ) + appBase = await Storage.new() + + // Setup constants + APP_MANAGER_ROLE = await kernelBase.APP_MANAGER_ROLE() + REGISTER_DATA_ROLE = await appBase.REGISTER_DATA_ROLE() + }) + + beforeEach(async () => { + const daoReceipt = await daoFact.newDAO(firstAccount) + const dao = Kernel.at( + daoReceipt.logs.filter(l => l.event === 'DeployDAO')[0].args.dao + ) + const acl = ACL.at(await dao.acl()) + + await acl.createPermission( + firstAccount, + dao.address, + APP_MANAGER_ROLE, + firstAccount, + { + from: firstAccount, + } + ) + + const receipt = await dao.newAppInstance( + '0x1234', + appBase.address, + '0x', + false, + { from: firstAccount } + ) + + app = Storage.at( + receipt.logs.filter(l => l.event === 'NewAppProxy')[0].args.proxy + ) + + await acl.createPermission( + ANY_ADDRESS, + app.address, + REGISTER_DATA_ROLE, + firstAccount, + { + from: firstAccount, + } + ) + }) + + it('should set a key-value pair for home app', async () => { + app.initialize() + await app.registerData(0x1e, '0xdeadcow', { from: firstAccount }) + assert.equal(await app.getRegisteredData(0x1e), '0xdeadcow') + }) +}) diff --git a/apps/storage/truffle-config.js b/apps/storage/truffle-config.js new file mode 100644 index 000000000..4459a90c1 --- /dev/null +++ b/apps/storage/truffle-config.js @@ -0,0 +1 @@ +module.exports = require("@aragon/os/truffle-config") \ No newline at end of file diff --git a/templates/dev/contracts/DevTemplate.sol b/templates/dev/contracts/DevTemplate.sol index 17dfdeeec..76296d8ee 100644 --- a/templates/dev/contracts/DevTemplate.sol +++ b/templates/dev/contracts/DevTemplate.sol @@ -77,6 +77,7 @@ contract DevTemplate is BaseOEApps { _transferCreatePaymentManagerFromTemplate(acl, finance, voting); _transferPermissionFromTemplate(acl, vault, vault.TRANSFER_ROLE(), voting); _transferRootPermissionsFromTemplateAndFinalizeDAO(dao, voting); + _setupStorageApp(dao, acl, voting); } /** @@ -220,6 +221,11 @@ contract DevTemplate is BaseOEApps { _createRewardsPermissions(_acl, _rewards, _tokenManager, _tokenManager); } + function _setupStorageApp(Kernel _dao, ACL _acl, Voting _voting) internal { + Storage storageApp = _installStorageApp(_dao); + _createStorageAppPermissions(_acl, storageApp, ANY_ENTITY, _voting); + } + function _validateDotSettings(uint64[3] memory _dotVotingSettings) private pure { require(_dotVotingSettings.length == 3, ERROR_BAD_DOT_VOTE_SETTINGS); } diff --git a/templates/open-enterprise/contracts/BaseOEApps.sol b/templates/open-enterprise/contracts/BaseOEApps.sol index be7c9caaa..0cedaa6c8 100644 --- a/templates/open-enterprise/contracts/BaseOEApps.sol +++ b/templates/open-enterprise/contracts/BaseOEApps.sol @@ -8,6 +8,7 @@ import "@tps/apps-discussions/contracts/DiscussionApp.sol"; import { DotVoting } from "@tps/apps-dot-voting/contracts/DotVoting.sol"; import "@tps/apps-projects/contracts/Projects.sol"; import "@tps/apps-rewards/contracts/Rewards.sol"; +import "../../../apps/storage/contracts/Storage.sol"; import "./BaseCache.sol"; @@ -19,6 +20,7 @@ contract BaseOEApps is BaseCache, TokenCache { bytes32 constant internal DISCUSSIONS_APP_ID = apmNamehash("discussions"); // discussions.aragonpm.eth; bytes32 constant internal DOT_VOTING_APP_ID = apmNamehash("dot-voting"); // dot-voting.aragonpm.eth; bytes32 constant internal PROJECTS_APP_ID = apmNamehash("projects"); // projects.aragonpm.eth; + bytes32 constant internal STORAGE_APP_ID = apmNamehash("storage"); // storage.aragonpm.eth bytes32 constant internal REWARDS_APP_ID = apmNamehash("rewards"); // rewards.aragonpm.eth; // */ // TODO: Move to HatchAPM // Main APM ? @@ -158,6 +160,15 @@ contract BaseOEApps is BaseCache, TokenCache { _acl.createPermission(_grantee, _projects, _projects.WORK_REVIEW_ROLE(), _manager); } + /* STORAGE */ + function _installStorageApp(Kernel _dao) internal returns (Storage) { + return Storage(_dao.newAppInstance(STORAGE_APP_ID, _latestVersionAppBase(STORAGE_APP_ID), new bytes(0), true)); + } + + function _createStorageAppPermissions(ACL _acl, Storage _storage, address _grantee, address _manager) internal { + _acl.createPermission(_grantee, _storage, _storage.REGISTER_DATA_ROLE(), _manager); + } + /* REWARDS */ function _installRewardsApp(Kernel _dao, Vault _vault) internal returns (Rewards) {