Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Survey: Add sample survey kit #915

Merged
merged 8 commits into from
Jul 22, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions apps/survey/contracts/examples/SurveyKit.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
pragma solidity 0.4.24;

import "@aragon/apps-vault/contracts/Vault.sol";
import "@aragon/os/contracts/acl/ACL.sol";
import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/kernel/KernelConstants.sol";
import "@aragon/os/contracts/apm/Repo.sol";
import "@aragon/os/contracts/apm/APMNamehash.sol";
import "@aragon/os/contracts/factory/DAOFactory.sol";
Expand Down Expand Up @@ -53,15 +55,20 @@ contract KitBase is APMNamehash, EVMScriptRegistryConstants {
}
}

contract SurveyKit is APMNamehash, KitBase {
contract SurveyKit is KernelAppIds, KitBase {
bytes32 constant public SURVEY_APP_ID = apmNamehash("survey"); // survey.aragonpm.eth

constructor(ENS ens) KitBase(DAOFactory(0), ens) public {}
event DeployInstance(address dao, address indexed token);

constructor(DAOFactory _fac, ENS _ens) KitBase(_fac, _ens) public {
// factory must be set up w/o EVMScript support
require(address(_fac.regFactory()) == address(0));
}

function newInstance(
MiniMeToken signalingToken,
address surveyManager,
address escapeHatch,
address escapeHatchOwner,
uint64 duration,
uint64 participation
)
Expand All @@ -74,23 +81,22 @@ contract SurveyKit is APMNamehash, KitBase {
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);

Survey survey = Survey(dao.newAppInstance(SURVEY_APP_ID, latestVersionAppBase(SURVEY_APP_ID)));

// Set escapeHatch address as the default vault, in case a token rescue is required
dao.setApp(dao.APP_BASES_NAMESPACE(), dao.recoveryVaultAppId(), escapeHatch);

survey.initialize(signalingToken, participation, duration);

// Install a vault to let it be the escape hatch
Vault vault = Vault(dao.newAppInstance(KERNEL_DEFAULT_VAULT_APP_ID, latestVersionAppBase(KERNEL_DEFAULT_VAULT_APP_ID)));
vault.initialize();
facuspagnuolo marked this conversation as resolved.
Show resolved Hide resolved

// Set survey manager as the entity that can create votes and change participation
// surveyManager can then give this permission to other entities
acl.createPermission(surveyManager, survey, survey.CREATE_SURVEYS_ROLE(), surveyManager);
acl.createPermission(surveyManager, survey, survey.MODIFY_PARTICIPATION_ROLE(), surveyManager);
acl.grantPermission(surveyManager, dao, dao.APP_MANAGER_ROLE());
acl.setPermissionManager(surveyManager, dao, dao.APP_MANAGER_ROLE());
acl.createPermission(escapeHatchOwner, vault, vault.TRANSFER_ROLE(), escapeHatchOwner);

cleanupDAOPermissions(dao, acl, surveyManager);

emit DeployInstance(dao);
emit InstalledApp(survey, SURVEY_APP_ID);
emit DeployInstance(dao, signalingToken);

return (dao, survey);
}
Expand Down
11 changes: 10 additions & 1 deletion apps/survey/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@
"prepare": "npx apps-shared-scripts-prepare",
"install:frontend": "cd app && npm install",
"abi:extract": "truffle-extract --output abi/ --keys abi",
"prepublishOnly": "truffle compile --all && npm run abi:extract -- --no-compile"
"prepublishOnly": "truffle compile --all && npm run abi:extract -- --no-compile",
"publish:kit:rpc": "aragon apm publish major $(npm run deploy:kit:rpc | tail -n 1) --environment default",
"deploy:kit:rpc": "aragon contracts compile --all && aragon contracts exec --network rpc scripts/ deploy.js",
"publish:kit:staging": "aragon apm publish major $(npm run deploy:kit:staging | tail -n 1) --environment staging",
"deploy:kit:staging": "aragon contracts compile --all && ENS=0xfe03625ea880a8cba336f9b5ad6e15b0a3b5a939 aragon contracts exec --network rinkeby scripts/ deploy.js",
"publish:kit:rinkeby": "aragon apm publish major $(npm run deploy:kit:rinkeby | tail -n 1) --environment rinkeby",
"deploy:kit:rinkeby": "aragon contracts compile --all && ENS=0x98Df287B6C145399Aaa709692c8D308357bC085D aragon contracts exec --network rinkeby scripts/ deploy.js",
"publish:kit:mainnet": "aragon apm publish major $(npm run deploy:kit:mainnet | tail -n 1) --environment mainnet",
"deploy:kit:mainnet": "aragon contracts compile --all && ENS=0x314159265dd8dbb310642f98f50c066173c1259b aragon contracts exec --network mainnet scripts/ deploy.js"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to not have most of these npm scripts, since the example kit is unlikely to be published very many times by us.

cc @0xGabi which scripts does aragen depend on?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I honestly don't know how they are used and if they are used in a certain way actually. I migrated all the scripts related stuff that was in the survey kit repo. Let me know if I should remove those

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aragen is only using the publish:kit:rpc and deploy:kit:rpc but I believe it would be posible to use aragonCLI directly in the bash file (here)

My only unknown is how to reference the different template deploy scripts, I believe something like $SCRIPT_NAME.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only unknown is how to reference the different template deploy scripts, I believe something like $SCRIPT_NAME.

Hmm, do you mean which script aragen would use? Perhaps we can standardize on a deploy_kit.js or something that will be used by aragen?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes exactly. You are right, we should standardize. Will check if that's possible with current deploy_kit.js

},
"files": [
"/abi",
Expand All @@ -31,6 +39,7 @@
"license": "(GPL-3.0-or-later OR AGPL-3.0-or-later)",
"description": "",
"devDependencies": {
"@aragon/apps-vault": "^4.0.0",
"@aragon/apps-shared-migrations": "1.0.0",
"@aragon/apps-shared-scripts": "^1.0.0",
"@aragon/cli": "~5.6.0",
Expand Down
238 changes: 238 additions & 0 deletions apps/survey/scripts/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
const namehash = require('eth-ens-namehash').hash

const ENS = artifacts.require('@aragon/os/contracts/lib/ens/ENS')
const MiniMeToken = artifacts.require('@aragon/os/contracts/lib/minime/MiniMeToken')
const MiniMeTokenFactory = artifacts.require('@aragon/os/contracts/lib/minime/MiniMeTokenFactory')
const PublicResolver = artifacts.require('@aragon/os/contracts/lib/ens/PublicResolver')
const Repo = artifacts.require('@aragon/os/contracts/apm/Repo')

const SurveyKit = artifacts.require('@aragon/kits-survey/contracts/SurveyKit')
const Survey = artifacts.require('@aragon/apps-survey/contracts/Survey')

// Utils
const surveyAppId = namehash('survey.aragonpm.eth')
const surveyKitEnsNode = namehash('survey-kit.aragonpm.eth')
const pct16 = x => new web3.BigNumber(x).times(new web3.BigNumber(10).toPower(16))
const createdSurveyDao = receipt => receipt.logs.filter(x => x.event === 'DeployInstance')[0].args.dao
const installedApp = (receipt, appId) => receipt.logs.filter(x => x.event === 'InstalledApp' && x.args.appId === appId)[0].args.appProxy

// Accounts (not held by root)
const accounts = [
'0x8401Eb5ff34cc943f096A32EF3d5113FEbE8D4Eb',
'0x306469457266CBBe7c0505e8Aad358622235e768',
'0xd873F6DC68e3057e4B7da74c6b304d0eF0B484C7',
'0xDcC5dD922fb1D0fd0c450a0636a8cE827521f0eD',
]

// Survey params
const TOKEN_BASE_DECIMAL = 1e18
const SURVEY_CHART_BLOCKS = 16 // front-end chart blocks
const SURVEY_DURATION = 60 * 60 * SURVEY_CHART_BLOCKS // one hour for each block
const SURVEY_PARTICIPATION = pct16(10) // 10%

module.exports = async () => {
console.log('Launching Survey demo')

if (process.argv.length < 5 || process.argv[3] !== '--network') {
console.error('Usage: truffle exec --network <network> scripts/launch-demo.js')
exit(1)
}
const owner = process.env.OWNER
const ens = ENS.at(
process.env.ENS ||
'0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1' // aragen's default ENS
)

console.log('ENS:', ens.address)
console.log('Owner:', owner)

const resolver = PublicResolver.at(await ens.resolver(surveyKitEnsNode))
const surveyKitRepo = Repo.at(await resolver.addr(surveyKitEnsNode))

// Contract address is second return of Repo.getLatest()
const surveyKit = SurveyKit.at((await surveyKitRepo.getLatest())[1])
console.log('SurveyKit:', surveyKit.address)

// Create minime token and assign to accounts
const minimeFac = await MiniMeTokenFactory.new()
const surveyToken = await MiniMeToken.new(minimeFac.address, '0x00', '0x00', 'Demo Survey', 18, 'SUR', true)

// Total of 40 tokens being assigned (10 to owner)
const tokenAssignments = [
TOKEN_BASE_DECIMAL * 16,
TOKEN_BASE_DECIMAL * 8,
TOKEN_BASE_DECIMAL * 4,
TOKEN_BASE_DECIMAL * 2,
TOKEN_BASE_DECIMAL * 10, // owner
]
await Promise.all(
tokenAssignments.map(async (amount, i) => {
let account
if (i < accounts.length) {
account = accounts[i]
} else {
account = owner
}

await surveyToken.generateTokens(account, amount)
console.log(`Assigned ${account} ${amount} tokens`)
})
)

// Create DAO with just Survey installed
console.log('Creating Survey DAO...')
const surveyDaoReceipt = await surveyKit.newInstance(surveyToken.address, owner, owner, SURVEY_DURATION, SURVEY_PARTICIPATION)
const surveyDaoAddr = createdSurveyDao(surveyDaoReceipt)
const surveyAppAddr = installedApp(surveyDaoReceipt, surveyAppId)

// Create some sample surveys and vote on them
const survey = Survey.at(surveyAppAddr)

const newSurvey = ({ question, description, options, url }) => {
const metadata = {
specId: namehash('1.metadata.survey.aragonpm.eth'),
metadata: {
question,
description,
options,
url,
}
}
return survey.newSurvey(JSON.stringify(metadata), options.length)
}
const voteSurvey = (surveyId, { options, stakes }, txOptions) => {
return survey.voteOptions(surveyId, options, stakes, txOptions)
}

// Start some surveys
console.log('Creating some surveys...')
const surveyMetadata = [
{
question: 'Should we get Carlos Matos?',
description: 'Coming to an event near you!',
options: ['OMG YES', ':sigh:', "You're telling me, he's real???"],
url: 'https://github.com/aragon/nest/issues/4',
},
{
question: 'When should the Aragon Network be launched?',
description: 'The Aragon Network is an upcoming launch by the Aragon team!',
options: ['Q1 2019', 'Q2 2019', 'Q3 2019', 'Q4 2019', 'Soon'],
url: 'https://github.com/aragon/nest/issues/1',
},
{
question: 'What should we name our 0.6 release?',
description: 'We want your help in naming it!',
options: ['Buidler', 'Engineer', 'Artist', 'Infinity'],
url: 'https://github.com/aragon/nest/issues/3',
},
{
question: 'Should ANT be redeployed as an ERC-777 token?',
description: 'The current ANT token is a MiniMe token, but this is costly in gas for most users.',
options: ['Yes', 'No'],
url: 'https://github.com/aragon/nest/issues/2',
},
]
const surveys = await Promise.all(
surveyMetadata.map(
metadata => newSurvey(metadata)
.then(receipt => ({
metadata,
id: receipt.logs.filter(x => x.event === 'StartSurvey')[0].args.surveyId,
}))
)
)

// Vote on the surveys—each account will vote for a few options at a specific
// point in time (to make them show up in different time buckets in the UI)
// REMEMBER: optionIds start from 1, not 0!
console.log('Voting on the surveys...')
const account1Votes = [
{
options: [2],
stakes: [TOKEN_BASE_DECIMAL * 2],
},
{
options: [5],
stakes: [TOKEN_BASE_DECIMAL * 5],
},
{
options: [1, 3],
stakes: [TOKEN_BASE_DECIMAL * 8, TOKEN_BASE_DECIMAL * 3],
},
]
await Promise.all(
account1Votes.map(
(voteParams, index) => voteSurvey(surveys[index].id, voteParams, { from: accounts[0] })
)
)
// Haha, this time travel idea doesn't actually work
// await timeTravel(SURVEY_DURATION / SURVEY_CHART_BLOCKS + 1)

const account2Votes = [
{
options: [1],
stakes: [TOKEN_BASE_DECIMAL * 1],
},
{
options: [1, 2],
stakes: [TOKEN_BASE_DECIMAL * 1, TOKEN_BASE_DECIMAL * 1],
},
{
options: [2],
stakes: [TOKEN_BASE_DECIMAL * 1],
},
]
await Promise.all(
account2Votes.map(
(voteParams, index) => voteSurvey(surveys[index].id, voteParams, { from: accounts[1] })
)
)
// await timeTravel(SURVEY_DURATION / SURVEY_CHART_BLOCKS + 1)

const account3Votes = [
{
options: [2],
stakes: [TOKEN_BASE_DECIMAL * 4],
},
{
options: [3, 4],
stakes: [TOKEN_BASE_DECIMAL * 2, TOKEN_BASE_DECIMAL * 1],
},
{
options: [2],
stakes: [TOKEN_BASE_DECIMAL * 1],
},
]
await Promise.all(
account3Votes.map(
(voteParams, index) => voteSurvey(surveys[index].id, voteParams, { from: accounts[2] })
)
)
// await timeTravel(SURVEY_DURATION / SURVEY_CHART_BLOCKS + 1)

const account4Votes = [
{
options: [1],
stakes: [TOKEN_BASE_DECIMAL * 1],
},
{
options: [1],
stakes: [TOKEN_BASE_DECIMAL * 1],
},
{
options: [1],
stakes: [TOKEN_BASE_DECIMAL * 1],
},
]
await Promise.all(
account4Votes.map(
(voteParams, index) => voteSurvey(surveys[index].id, voteParams, { from: accounts[3] })
)
)

console.log('===========')
console.log('Survey demo DAO set up!')
console.log('Survey DAO:', surveyDaoAddr)
console.log("Survey DAO's Survey app:", surveyAppAddr)
console.log('Survey Token:', surveyToken.address)
}
Loading