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

aragonOS 4 - Payroll kits - Payroll kits demo #1

Open
wants to merge 10 commits into
base: aragonos-4-payroll-kits-base
Choose a base branch
from
15 changes: 15 additions & 0 deletions kits/payroll-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Demo Payroll Kit
===============

Usage
-----

This kit requires you to already have the [Payroll Kit](../payroll) deployed onto a local chain.

`$ aragon devchain`

Inside [Payroll Kit](../payroll) `$ npm run migrate`

Once you've deployed the base Payroll Kit, you can simply run this kit's migration

`npm run migrate`.
10 changes: 10 additions & 0 deletions kits/payroll-demo/contracts/PPF.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pragma solidity ^0.4.24;

import "@aragon/ppf-contracts/contracts/IFeed.sol";

contract PPF is IFeed {
function get(address base, address quote) external view returns (uint128 xrt, uint64 when) {
xrt = 1;
when = uint64(now);
}
}
9 changes: 9 additions & 0 deletions kits/payroll-demo/contracts/PayrollDemoImports.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity 0.4.24;

// HACK to workaround truffle artifact loading on dependencies

import "@aragon/kits-payroll/contracts/Imports.sol";
import "@aragon/kits-payroll/contracts/PayrollKit.sol";
import "@aragon/apps-shared-minime/contracts/MiniMeToken.sol";

contract PayrollDemoImports {}
24 changes: 24 additions & 0 deletions kits/payroll-demo/contracts/misc/Migrations.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pragma solidity 0.4.24;

contract Migrations {
address public owner;
uint public last_completed_migration;

modifier restricted() {
if (msg.sender == owner)
_;
}

function Migrations() public {
owner = msg.sender;
}

function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}

function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
5 changes: 5 additions & 0 deletions kits/payroll-demo/migrations/1_initial_migration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var Migrations = artifacts.require("./Migrations.sol");

module.exports = function(deployer) {
deployer.deploy(Migrations);
};
77 changes: 77 additions & 0 deletions kits/payroll-demo/migrations/2_deploy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const path = require('path')
const fs = require('fs')

const namehash = require('eth-ens-namehash').hash

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

const PayrollKit = artifacts.require('@aragon/kits-payroll/contracts/PayrollKit')
const Payroll = artifacts.require('@aragon/future-apps-payroll/contracts/Payroll')

// Utils
const payrollAppId = namehash('payroll.aragonpm.eth')
const payrollKitEnsNode = namehash('payroll-kit.aragonpm.eth')
const timeTravel = require('@aragon/test-helpers/timeTravel')(web3)
const pct16 = x => new web3.BigNumber(x).times(new web3.BigNumber(10).toPower(16))
const createdPayrollDao = receipt => receipt.logs.filter(x => x.event == 'DeployInstance')[0].args.dao
const createdPayrollId = receipt => receipt.logs.filter(x => x.event == 'StartPayroll')[0].args.payroId
const installedApp = (receipt, appId) => receipt.logs.filter(x => x.event == 'InstalledApp' && x.args.appId === appId)[0].args.appProxy

module.exports = (deployer, network, accounts) => {
deployer.then(async () => {
const root = accounts[0]

const ens = ENS.at(
process.env.ENS ||
'0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1' // aragen's default ENS
)
const payrollKitRepo = Repo.at(
await PublicResolver.at(
await ens.resolver(payrollKitEnsNode)
).addr(payrollKitEnsNode)
)
// Contract address is second return of Repo.getLatest()
const payrollKit = PayrollKit.at((await payrollKitRepo.getLatest())[1])

// const minimeFac = await MiniMeTokenFactory.new()
const denominationToken = await MiniMeToken.new(
'0x00',
'0x00',
'0x00',
'USD Dolar',
18,
'USD',
true
)

const ppf = await artifacts.require('PPF').new()

const SECONDS_IN_A_YEAR = 31557600 // 365.25 days
const RATE_EXPIRY_TIME = 1000

// Create DAO with Payroll installed
console.log('Creating Payroll DAO...')
const payrollDaoReceipt = await payrollKit.newInstance(
root,
root,
SECONDS_IN_A_YEAR,
denominationToken.address,
ppf.address,
RATE_EXPIRY_TIME
)

const payrollDaoAddr = createdPayrollDao(payrollDaoReceipt)
const payrollAppAddr = installedApp(payrollDaoReceipt, payrollAppId)

// TODO: Create some sample data

console.log('===========')
console.log('Payroll demo DAO set up!')
console.log('Payroll DAO:', payrollDaoAddr)
console.log("Payroll DAO's Payroll app:", payrollAppAddr)
console.log('Payroll Token:', denominationToken.address)
})
}
31 changes: 31 additions & 0 deletions kits/payroll-demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@aragon/kits-payroll-demo",
"version": "1.0.0",
"description": "Demo kit for setting up a DAO to do Payroll",
"main": "index.js",
"directories": {
"test": "test"
},
"scripts": {
"migrate": "truffle migrate --network rpc --reset"
},
"author": "Aragon Institution MTU <[email protected]>",
"license": "GPL-3.0",
"devDependencies": {
"eth-ens-namehash": "^2.0.8",
"truffle": "4.1.14",
"truffle-hdwallet-provider": "0.0.3"
},
"dependencies": {
"@aragon/kits-bare": "1.0.0",
"@aragon/kits-payroll": "1.0.0",
"@aragon/apps-shared-minime": "^1.0.0",
"@aragon/apps-finance": "^2.0.0-beta.2",
"@aragon/apps-vault": "^3.0.0-beta.2",
"@aragon/future-apps-payroll": "0.1.0",
"@aragon/test-helpers": "^1.0.1",
"@aragon/ppf-contracts": "^1.0.2",
"@aragon/ppf.js": "1.0.1",
"@aragon/os": "^4.0.0-beta.3"
}
}
6 changes: 6 additions & 0 deletions kits/payroll-demo/truffle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let x = require("@aragon/os/truffle-config")

x.networks.rinkeby.gasPrice = 25000000001
x.networks.rinkeby.gas = 7e6

module.exports = x
2 changes: 1 addition & 1 deletion kits/payroll/contracts/Imports.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity 0.4.18;
pragma solidity 0.4.24;

// HACK to workaround truffle artifact loading on dependencies

Expand Down
129 changes: 64 additions & 65 deletions kits/payroll/contracts/PayrollKit.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity 0.4.18;
pragma solidity 0.4.24;

import "@aragon/os/contracts/kernel/Kernel.sol";
import "@aragon/os/contracts/acl/ACL.sol";
Expand All @@ -11,84 +11,83 @@ import "@aragon/apps-vault/contracts/Vault.sol";
import "@aragon/kits-bare/contracts/KitBase.sol";


contract PayrollKit is KitBase {
function PayrollKit(DAOFactory _fac, ENS _ens) KitBase(_fac, _ens) {}
contract PayrollKit is KitBase, APMNamehash {
constructor(
DAOFactory _fac,
ENS _ens
)
KitBase(_fac, _ens)
public
{}

function newInstance(
address employer,
address root,
uint64 financePeriodDuration,
address denominationToken,
IFeed priceFeed,
IFeed priceFeed,
uint64 rateExpiryTime
) returns (Kernel dao, Payroll payroll) {
dao = fac.newDAO(this);
ACL acl = ACL(dao.acl());

acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);

Vault vault;
Finance finance;
(vault, finance, payroll) = deployApps(dao);

finance.initialize(vault, financePeriodDuration);
payroll.initialize(finance, denominationToken, priceFeed, rateExpiryTime);

// Payroll permissions
acl.createPermission(employer, payroll, payroll.ADD_EMPLOYEE_ROLE(), root);
acl.createPermission(employer, payroll, payroll.REMOVE_EMPLOYEE_ROLE(), root);
acl.createPermission(employer, payroll, payroll.ALLOWED_TOKENS_MANAGER_ROLE(), root);
acl.createPermission(root, payroll, payroll.CHANGE_PRICE_FEED_ROLE(), root);
acl.createPermission(root, payroll, payroll.MODIFY_RATE_EXPIRY_ROLE(), root);

// Finance permissions
acl.createPermission(payroll, finance, finance.CREATE_PAYMENTS_ROLE(), root);

// Vault permissions
bytes32 vaultTransferRole = vault.TRANSFER_ROLE();
acl.createPermission(finance, vault, vaultTransferRole, this); // manager is this to allow 2 grants
acl.grantPermission(root, vault, vaultTransferRole);
acl.setPermissionManager(root, vault, vaultTransferRole); // set root as the final manager for the role

cleanupDAOPermissions(dao, acl, root);

DeployInstance(dao);
}
)
public
returns (Kernel dao, Payroll payroll)
{
dao = fac.newDAO(this);
ACL acl = ACL(dao.acl());

function deployApps(Kernel dao) internal returns (Vault, Finance, Payroll) {
bytes32 vaultAppId = apmNamehash("vault");
bytes32 financeAppId = apmNamehash("finance");
bytes32 payrollAppId = apmNamehash("payroll");
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);

Vault vault = Vault(dao.newAppInstance(vaultAppId, latestVersionAppBase(vaultAppId)));
Finance finance = Finance(dao.newAppInstance(financeAppId, latestVersionAppBase(financeAppId)));
Payroll payroll = Payroll(dao.newAppInstance(payrollAppId, latestVersionAppBase(payrollAppId)));
Vault vault;
Finance finance;
(vault, finance, payroll) = deployApps(dao);

InstalledApp(vault, vaultAppId);
InstalledApp(finance, financeAppId);
InstalledApp(payroll, payrollAppId);
finance.initialize(vault, financePeriodDuration);
payroll.initialize(finance, denominationToken, priceFeed, rateExpiryTime);

return (vault, finance, payroll);
}
// Payroll permissions
acl.createPermission(employer, payroll, payroll.ADD_EMPLOYEE_ROLE(), root);
acl.createPermission(employer, payroll, payroll.TERMINATE_EMPLOYEE_ROLE(), root);
acl.createPermission(employer, payroll, payroll.ALLOWED_TOKENS_MANAGER_ROLE(), root);
acl.createPermission(employer, payroll, payroll.SET_EMPLOYEE_SALARY_ROLE(), root);
acl.createPermission(employer, payroll, payroll.ADD_ACCRUED_VALUE_ROLE(), root);
acl.createPermission(root, payroll, payroll.CHANGE_PRICE_FEED_ROLE(), root);
acl.createPermission(root, payroll, payroll.MODIFY_RATE_EXPIRY_ROLE(), root);

// Finance permissions
acl.createPermission(payroll, finance, finance.CREATE_PAYMENTS_ROLE(), root);

// Vault permissions
setVaultPermissions(acl, vault, finance, root);

/// EVMScriptRegistry permissions
EVMScriptRegistry reg = EVMScriptRegistry(dao.getApp(dao.APP_ADDR_NAMESPACE(), EVMSCRIPT_REGISTRY_APP_ID));
acl.createBurnedPermission(reg, reg.REGISTRY_ADD_EXECUTOR_ROLE());
acl.createBurnedPermission(reg, reg.REGISTRY_MANAGER_ROLE());

function cleanupDAOPermissions(Kernel dao, ACL acl, address root) internal {
bytes32 daoAppManagerRole = dao.APP_MANAGER_ROLE();
// Kernel permission clean up
acl.grantPermission(root, dao, daoAppManagerRole);
acl.revokePermission(this, dao, daoAppManagerRole);
acl.setPermissionManager(root, dao, daoAppManagerRole);

// ACL permission clean up
bytes32 aclCreatePermissionsRole = acl.CREATE_PERMISSIONS_ROLE();
acl.grantPermission(root, acl, aclCreatePermissionsRole);
acl.revokePermission(this, acl, aclCreatePermissionsRole);
acl.setPermissionManager(root, acl, aclCreatePermissionsRole);
cleanupDAOPermissions(dao, acl, root);

emit DeployInstance(dao);
}

function latestVersionAppBase(bytes32 appId) public view returns (address base) {
Repo repo = Repo(PublicResolver(ens.resolver(appId)).addr(appId));
(,base,) = repo.getLatest();
function deployApps(Kernel dao) internal returns (Vault, Finance, Payroll) {
bytes32 vaultAppId = apmNamehash("vault");
bytes32 financeAppId = apmNamehash("finance");
bytes32 payrollAppId = apmNamehash("payroll");

Vault vault = Vault(dao.newAppInstance(vaultAppId, latestVersionAppBase(vaultAppId)));
Finance finance = Finance(dao.newAppInstance(financeAppId, latestVersionAppBase(financeAppId)));
Payroll payroll = Payroll(dao.newAppInstance(payrollAppId, latestVersionAppBase(payrollAppId)));

emit InstalledApp(vault, vaultAppId);
emit InstalledApp(finance, financeAppId);
emit InstalledApp(payroll, payrollAppId);

return (vault, finance, payroll);
}

return base;
function setVaultPermissions(ACL acl, Vault vault, Finance finance, address root) internal {
bytes32 vaultTransferRole = vault.TRANSFER_ROLE();
acl.createPermission(finance, vault, vaultTransferRole, this); // manager is this to allow 2 grants
acl.grantPermission(root, vault, vaultTransferRole);
acl.setPermissionManager(root, vault, vaultTransferRole); // set root as the final manager for the role
}
}
33 changes: 17 additions & 16 deletions kits/payroll/contracts/misc/Migrations.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
pragma solidity ^0.4.17;
pragma solidity 0.4.24;

contract Migrations {
address public owner;
uint public last_completed_migration;
address public owner;
uint public last_completed_migration;

modifier restricted() {
if (msg.sender == owner) _;
}
modifier restricted() {
if (msg.sender == owner)
_;
}

function Migrations() public {
owner = msg.sender;
}
function Migrations() public {
owner = msg.sender;
}

function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}

function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
Loading