ByeBye apps
This commit is contained in:
parent
dd70bf77d5
commit
f5ca44d779
@ -1,12 +0,0 @@
|
||||
module.exports = {
|
||||
'globals': {
|
||||
contract: true,
|
||||
describe: true,
|
||||
it: true,
|
||||
},
|
||||
rules: {
|
||||
'no-unused-vars': ['error', {
|
||||
'argsIgnorePattern': '^_',
|
||||
}],
|
||||
}
|
||||
}
|
4
apps/contribution/.gitignore
vendored
4
apps/contribution/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
build
|
||||
.cache
|
||||
dist
|
@ -1,14 +0,0 @@
|
||||
# Git files
|
||||
.gitignore
|
||||
|
||||
# Build files
|
||||
.cache
|
||||
node_modules
|
||||
build
|
||||
|
||||
# Lock files
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Others
|
||||
test
|
@ -1 +0,0 @@
|
||||
# Kredits Contribution
|
@ -1,38 +0,0 @@
|
||||
{
|
||||
"roles": [
|
||||
{
|
||||
"name": "Add contributions",
|
||||
"id": "ADD_CONTRIBUTION_ROLE",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Manage token contract",
|
||||
"id": "MANAGE_TOKEN_CONTRACT_ROLE",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Veto contributions",
|
||||
"id": "VETO_CONTRIBUTION_ROLE",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-contribution.open.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
"appName": "kredits-contribution.open.aragonpm.eth",
|
||||
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||
"network": "rinkeby"
|
||||
},
|
||||
"production": {
|
||||
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||
"appName": "contribution.open.aragonpm.eth",
|
||||
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||
"network": "mainnet"
|
||||
}
|
||||
},
|
||||
"path": "contracts/Contribution.sol"
|
||||
}
|
@ -1,206 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||
|
||||
interface IToken {
|
||||
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public;
|
||||
}
|
||||
|
||||
interface ContributorInterface {
|
||||
function getContributorAddressById(uint32 contributorId) public view returns (address);
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
|
||||
// TODO Maybe use for validation
|
||||
// function exists(uint32 contributorId) public view returns (bool);
|
||||
}
|
||||
|
||||
contract Contribution is AragonApp {
|
||||
bytes32 public constant ADD_CONTRIBUTION_ROLE = keccak256("ADD_CONTRIBUTION_ROLE");
|
||||
bytes32 public constant VETO_CONTRIBUTION_ROLE = keccak256("VETO_CONTRIBUTION_ROLE");
|
||||
|
||||
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
|
||||
struct ContributionData {
|
||||
uint32 contributorId;
|
||||
uint32 amount;
|
||||
bool claimed;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
string tokenMetadataURL;
|
||||
uint256 confirmedAtBlock;
|
||||
bool vetoed;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
string internal name_;
|
||||
string internal symbol_;
|
||||
|
||||
// map contribution ID to contributor
|
||||
mapping(uint32 => uint32) public contributionOwner;
|
||||
// map contributor to contribution IDs
|
||||
mapping(uint32 => uint32[]) public ownedContributions;
|
||||
|
||||
mapping(uint32 => ContributionData) public contributions;
|
||||
uint32 public contributionsCount;
|
||||
|
||||
uint32 public blocksToWait;
|
||||
|
||||
event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount);
|
||||
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
|
||||
event ContributionVetoed(uint32 id, address vetoedByAccount);
|
||||
|
||||
function initialize(bytes32[5] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
blocksToWait = 40320; // 7 days; 15 seconds block time
|
||||
initialized();
|
||||
}
|
||||
|
||||
function getContract(uint8 appId) public view returns (address) {
|
||||
IKernel k = IKernel(kernel());
|
||||
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
|
||||
}
|
||||
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint32) {
|
||||
address contributorContract = getContract(uint8(Apps.Contributor));
|
||||
return ContributorInterface(contributorContract).getContributorIdByAddress(contributorAccount);
|
||||
}
|
||||
|
||||
function getContributorAddressById(uint32 contributorId) public view returns (address) {
|
||||
address contributorContract = getContract(uint8(Apps.Contributor));
|
||||
return ContributorInterface(contributorContract).getContributorAddressById(contributorId);
|
||||
}
|
||||
|
||||
//
|
||||
// Token standard functions (ERC 721)
|
||||
//
|
||||
|
||||
function name() external view returns (string) {
|
||||
return name_;
|
||||
}
|
||||
|
||||
function symbol() external view returns (string) {
|
||||
return symbol_;
|
||||
}
|
||||
|
||||
// Balance is amount of ERC271 tokens, not amount of kredits
|
||||
function balanceOf(address owner) public view returns (uint256) {
|
||||
require(owner != address(0));
|
||||
uint32 contributorId = getContributorIdByAddress(owner);
|
||||
return ownedContributions[contributorId].length;
|
||||
}
|
||||
|
||||
function ownerOf(uint32 contributionId) public view returns (address) {
|
||||
require(exists(contributionId));
|
||||
uint32 contributorId = contributions[contributionId].contributorId;
|
||||
return getContributorAddressById(contributorId);
|
||||
}
|
||||
|
||||
function tokenOfOwnerByIndex(address owner, uint32 index) public view returns (uint32) {
|
||||
uint32 contributorId = getContributorIdByAddress(owner);
|
||||
return ownedContributions[contributorId][index];
|
||||
}
|
||||
|
||||
function tokenMetadata(uint32 contributionId) public view returns (string) {
|
||||
return contributions[contributionId].tokenMetadataURL;
|
||||
}
|
||||
|
||||
//
|
||||
// Custom functions
|
||||
//
|
||||
|
||||
function totalKreditsEarned(bool confirmedOnly) public view returns (uint32 amount) {
|
||||
for (uint32 i = 1; i <= contributionsCount; i++) {
|
||||
ContributionData memory c = contributions[i];
|
||||
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
|
||||
amount += c.amount; // should use safemath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) public view returns (uint32 amount) {
|
||||
uint256 tokenCount = ownedContributions[contributorId].length;
|
||||
for (uint256 i = 0; i < tokenCount; i++) {
|
||||
uint32 cId = ownedContributions[contributorId][i];
|
||||
ContributionData memory c = contributions[cId];
|
||||
if (!c.vetoed && (block.number >= c.confirmedAtBlock || !confirmedOnly)) {
|
||||
amount += c.amount; // should use safemath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
|
||||
id = contributionId;
|
||||
ContributionData storage c = contributions[id];
|
||||
return (
|
||||
id,
|
||||
c.contributorId,
|
||||
c.amount,
|
||||
c.claimed,
|
||||
c.hashDigest,
|
||||
c.hashFunction,
|
||||
c.hashSize,
|
||||
c.confirmedAtBlock,
|
||||
c.exists,
|
||||
c.vetoed
|
||||
);
|
||||
}
|
||||
|
||||
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_CONTRIBUTION_ROLE) {
|
||||
//require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint32[](0)), 'nope');
|
||||
uint32 contributionId = contributionsCount + 1;
|
||||
ContributionData storage c = contributions[contributionId];
|
||||
c.exists = true;
|
||||
c.amount = amount;
|
||||
c.claimed = false;
|
||||
c.contributorId = contributorId;
|
||||
c.hashDigest = hashDigest;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
if (contributionId < 10) {
|
||||
c.confirmedAtBlock = block.number;
|
||||
} else {
|
||||
c.confirmedAtBlock = block.number + 1 + blocksToWait;
|
||||
}
|
||||
|
||||
contributionsCount++;
|
||||
|
||||
contributionOwner[contributionId] = contributorId;
|
||||
ownedContributions[contributorId].push(contributionId);
|
||||
|
||||
emit ContributionAdded(contributionId, contributorId, amount);
|
||||
}
|
||||
|
||||
function veto(uint32 contributionId) public isInitialized auth(VETO_CONTRIBUTION_ROLE) {
|
||||
ContributionData storage c = contributions[contributionId];
|
||||
require(c.exists, 'NOT_FOUND');
|
||||
require(!c.claimed, 'ALREADY_CLAIMED');
|
||||
require(block.number < c.confirmedAtBlock, 'VETO_PERIOD_ENDED');
|
||||
c.vetoed = true;
|
||||
|
||||
emit ContributionVetoed(contributionId, msg.sender);
|
||||
}
|
||||
|
||||
function claim(uint32 contributionId) public isInitialized {
|
||||
ContributionData storage c = contributions[contributionId];
|
||||
require(c.exists, 'NOT_FOUND');
|
||||
require(!c.claimed, 'ALREADY_CLAIMED');
|
||||
require(!c.vetoed, 'VETOED');
|
||||
require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE');
|
||||
|
||||
c.claimed = true;
|
||||
address tokenContract = getContract(uint8(Apps.Token));
|
||||
address contributorAccount = getContributorAddressById(c.contributorId);
|
||||
uint256 amount = uint256(c.amount);
|
||||
IToken(tokenContract).mintFor(contributorAccount, amount, contributionId);
|
||||
emit ContributionClaimed(contributionId, c.contributorId, c.amount);
|
||||
}
|
||||
|
||||
function exists(uint32 contributionId) public view returns (bool) {
|
||||
return contributions[contributionId].exists;
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
constructor() 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);
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "Contribution",
|
||||
"description": "Kredits contribution app"
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Migrations = artifacts.require('./Migrations.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Migrations)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Contribution = artifacts.require('Contribution.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Contribution)
|
||||
}
|
7900
apps/contribution/package-lock.json
generated
7900
apps/contribution/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "kredits-contribution",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@aragon/os": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aragon/test-helpers": "^2.1.0",
|
||||
"eth-gas-reporter": "^0.2.17",
|
||||
"ganache-cli": "^6.9.1",
|
||||
"solidity-coverage": "^0.5.11"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
"build:script": "",
|
||||
"build": "",
|
||||
"publish:patch": "aragon apm publish patch",
|
||||
"publish:minor": "aragon apm publish minor",
|
||||
"publish:major": "aragon apm publish major",
|
||||
"versions": "aragon apm versions",
|
||||
"test": "truffle test"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// const Contribution = artifacts.require('Contribution.sol');
|
||||
|
||||
contract('Contribution', (_accounts) => {
|
||||
it('should be tested');
|
||||
});
|
@ -1 +0,0 @@
|
||||
module.exports = require("../../truffle.js");
|
4
apps/contributor/.gitignore
vendored
4
apps/contributor/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
build
|
||||
.cache
|
||||
dist
|
@ -1,14 +0,0 @@
|
||||
# Git files
|
||||
.gitignore
|
||||
|
||||
# Build files
|
||||
.cache
|
||||
node_modules
|
||||
build
|
||||
|
||||
# Lock files
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Others
|
||||
test
|
@ -1 +0,0 @@
|
||||
# Kredits Contributor
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"roles": [
|
||||
{
|
||||
"name": "Manage contributors",
|
||||
"id": "MANAGE_CONTRIBUTORS_ROLE",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-contributor.open.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
"appName": "kredits-contributor.open.aragonpm.eth",
|
||||
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||
"network": "rinkeby"
|
||||
},
|
||||
"production": {
|
||||
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||
"appName": "contributor.open.aragonpm.eth",
|
||||
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||
"network": "mainnet"
|
||||
}
|
||||
},
|
||||
"path": "contracts/Contributor.sol"
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||
|
||||
interface ITokenBalance {
|
||||
function balanceOf(address contributorAccount) external view returns (uint256);
|
||||
}
|
||||
interface IContributionBalance {
|
||||
function totalKreditsEarnedByContributor(uint32 contributorId, bool confirmedOnly) external view returns (uint32 amount);
|
||||
function balanceOf(address owner) external view returns (uint256);
|
||||
}
|
||||
|
||||
contract Contributor is Initializable {
|
||||
IContributionBalance contributionContract;
|
||||
ITokenBalance tokenContract;
|
||||
|
||||
struct Contributor {
|
||||
address account;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
mapping (address => uint32) public contributorIds;
|
||||
mapping (uint32 => Contributor) public contributors;
|
||||
uint32 public contributorsCount;
|
||||
|
||||
event ContributorProfileUpdated(uint32 id, bytes32 oldHashDigest, bytes32 newHashDigest); // what should be logged
|
||||
event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount);
|
||||
event ContributorAdded(uint32 id, address account);
|
||||
|
||||
|
||||
function initialize() public initializer {
|
||||
|
||||
}
|
||||
|
||||
// TODO who can call this when?
|
||||
function setContributionContract(address contribution) public {
|
||||
contributionContract = IContributionBalance(contribution);
|
||||
}
|
||||
|
||||
// TODO who can call this when?
|
||||
function setTokenContract(address token) public {
|
||||
tokenContract = ITokenBalance(token);
|
||||
}
|
||||
|
||||
function coreContributorsCount() public view returns (uint32) {
|
||||
uint32 count = 0;
|
||||
for (uint32 i = 1; i <= contributorsCount; i++) {
|
||||
if (isCoreTeam(i)) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function updateContributorAccount(uint32 id, address oldAccount, address newAccount) public {
|
||||
require(newAccount != address(0), "invalid new account address");
|
||||
require(getContributorAddressById(id) == oldAccount, "contributor does not exist");
|
||||
|
||||
contributorIds[oldAccount] = 0;
|
||||
contributorIds[newAccount] = id;
|
||||
contributors[id].account = newAccount;
|
||||
emit ContributorAccountUpdated(id, oldAccount, newAccount);
|
||||
}
|
||||
|
||||
function updateContributorProfileHash(uint32 id, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public {
|
||||
Contributor storage c = contributors[id];
|
||||
bytes32 oldHashDigest = c.hashDigest;
|
||||
c.hashDigest = hashDigest;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
|
||||
ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
|
||||
}
|
||||
|
||||
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public {
|
||||
require(!addressExists(account));
|
||||
uint32 _id = contributorsCount + 1;
|
||||
assert(!contributors[_id].exists); // this can not be acually
|
||||
Contributor storage c = contributors[_id];
|
||||
c.exists = true;
|
||||
c.hashDigest = hashDigest;
|
||||
c.hashFunction = hashFunction;
|
||||
c.hashSize = hashSize;
|
||||
c.account = account;
|
||||
contributorIds[account] = _id;
|
||||
|
||||
contributorsCount += 1;
|
||||
emit ContributorAdded(_id, account);
|
||||
}
|
||||
|
||||
function isCoreTeam(uint32 id) view public returns (bool) {
|
||||
// TODO: for simplicity we simply define the first contributors as core
|
||||
// later this needs to be changed to something more dynamic
|
||||
return id < 7;
|
||||
}
|
||||
|
||||
function exists(uint32 id) view public returns (bool) {
|
||||
return contributors[id].exists;
|
||||
}
|
||||
|
||||
function addressIsCore(address account) view public returns (bool) {
|
||||
uint32 id = getContributorIdByAddress(account);
|
||||
return isCoreTeam(id);
|
||||
}
|
||||
|
||||
function addressExists(address account) view public returns (bool) {
|
||||
return getContributorByAddress(account).exists;
|
||||
}
|
||||
|
||||
function getContributorIdByAddress(address account) view public returns (uint32) {
|
||||
return contributorIds[account];
|
||||
}
|
||||
|
||||
function getContributorAddressById(uint32 id) view public returns (address) {
|
||||
return contributors[id].account;
|
||||
}
|
||||
|
||||
function getContributorByAddress(address account) internal view returns (Contributor memory) {
|
||||
uint32 id = contributorIds[account];
|
||||
return contributors[id];
|
||||
}
|
||||
|
||||
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, uint32 totalKreditsEarned, uint256 contributionsCount, bool exists ) {
|
||||
id = _id;
|
||||
Contributor storage c = contributors[_id];
|
||||
account = c.account;
|
||||
hashDigest = c.hashDigest;
|
||||
hashFunction = c.hashFunction;
|
||||
hashSize = c.hashSize;
|
||||
isCore = isCoreTeam(id);
|
||||
balance = tokenContract.balanceOf(c.account);
|
||||
totalKreditsEarned = contributionContract.totalKreditsEarnedByContributor(_id, true);
|
||||
contributionsCount = contributionContract.balanceOf(c.account);
|
||||
exists = c.exists;
|
||||
}
|
||||
|
||||
function canPerform(address _who, address _where, bytes32 _what, uint256[] memory _how) public returns (bool) {
|
||||
address sender = _who;
|
||||
if (sender == address(0)) {
|
||||
sender = tx.origin;
|
||||
}
|
||||
// _what == keccak256('VOTE_PROPOSAL_ROLE')
|
||||
if (_what == 0xd61216798314d2fc33e42ff2021d66707b1e38517d3f7166798a9d3a196a9c96) {
|
||||
return contributorIds[sender] != uint256(0);
|
||||
}
|
||||
|
||||
return addressIsCore(sender);
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
constructor() 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);
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/acl/ACL.sol";
|
||||
import "@aragon/os/contracts/kernel/Kernel.sol";
|
||||
import "@aragon/os/contracts/factory/DAOFactory.sol";
|
||||
|
||||
// You might think this file is a bit odd, but let me explain.
|
||||
// We only use for now those imported contracts in our tests, which
|
||||
// means Truffle will not compile them for us, because they are from
|
||||
// an external dependency.
|
||||
|
||||
|
||||
// solium-disable-next-line no-empty-blocks
|
||||
contract Spoof {
|
||||
// ...
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
require("@nomiclabs/hardhat-waffle");
|
||||
require('hardhat-deploy');
|
||||
require("hardhat-deploy-ethers");
|
||||
require('@openzeppelin/hardhat-upgrades');
|
||||
|
||||
const promptly = require('promptly');
|
||||
|
||||
// This is a sample Hardhat task. To learn how to create your own go to
|
||||
// https://hardhat.org/guides/create-task.html
|
||||
task("accounts", "Prints the list of accounts", async () => {
|
||||
const accounts = await ethers.getSigners();
|
||||
|
||||
for (const account of accounts) {
|
||||
console.log(account.address);
|
||||
}
|
||||
});
|
||||
|
||||
task('fund', "Send eth to an address", async () => {
|
||||
const to = await promptly.prompt('Address:');
|
||||
const value = await promptly.prompt('Value:');
|
||||
|
||||
const signer = await ethers.getSigners();
|
||||
|
||||
const fundTransaction = await signer[0].sendTransaction({to: to, value: ethers.utils.parseEther(value)});
|
||||
console.log(fundTransaction);
|
||||
});
|
||||
|
||||
task("create-wallet", "Creates a new wallet json", async () => {
|
||||
const wallet = ethers.Wallet.createRandom();
|
||||
|
||||
console.log('New wallet:');
|
||||
console.log(`Address: ${wallet.address}`);
|
||||
console.log(`Public key: ${wallet.publicKey}`);
|
||||
console.log(`Private key: ${wallet.privateKey}`);
|
||||
console.log(`Mnemonic: ${JSON.stringify(wallet.mnemonic)}`);
|
||||
|
||||
const password = await promptly.prompt('Encryption password: ')
|
||||
const encryptedJSON = await wallet.encrypt(password);
|
||||
|
||||
console.log('Encrypted wallet JSON:');
|
||||
console.log(encryptedJSON);
|
||||
});
|
||||
|
||||
// You need to export an object to set up your config
|
||||
// Go to https://hardhat.org/config/ to learn more
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
module.exports = {
|
||||
solidity: "0.8.0",
|
||||
// defaultNetwork: 'localhost',
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
}
|
||||
},
|
||||
namedAccounts: {
|
||||
deployer: {
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
};
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "Contributor",
|
||||
"description": "Kredits Contributor app"
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Migrations = artifacts.require('./Migrations.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Migrations)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Contributor = artifacts.require('Contributor.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Contributor)
|
||||
}
|
5064
apps/contributor/package-lock.json
generated
5064
apps/contributor/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,34 +0,0 @@
|
||||
{
|
||||
"name": "kredits-contributor",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.0",
|
||||
"@openzeppelin/contracts-upgradeable": "^4.1.0",
|
||||
"hardhat": "^2.0.3",
|
||||
"hardhat-deploy": "^0.7.0-beta.35",
|
||||
"hardhat-deploy-ethers": "^0.3.0-beta.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.2",
|
||||
"@openzeppelin/hardhat-upgrades": "^1.8.2",
|
||||
"ethers": "^5.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
"build:script": "",
|
||||
"build": "",
|
||||
"publish:patch": "aragon apm publish patch",
|
||||
"publish:minor": "aragon apm publish minor",
|
||||
"publish:major": "aragon apm publish major",
|
||||
"versions": "aragon apm versions",
|
||||
"test": "truffle test"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
const { ethers, upgrades } = require("hardhat");
|
||||
|
||||
async function main() {
|
||||
const Contributor = await ethers.getContractFactory("Contributor");
|
||||
const contributor = await upgrades.deployProxy(Contributor, []);
|
||||
await contributor.deployed();
|
||||
console.log("Contributor deployed to:", contributor.address);
|
||||
}
|
||||
|
||||
main();
|
@ -1,170 +0,0 @@
|
||||
const namehash = require('ethers').utils.namehash;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const Contributor = artifacts.require("Contributor.sol");
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getContract = name => artifacts.require(name);
|
||||
const { assertRevert } = require('@aragon/test-helpers/assertThrow');
|
||||
|
||||
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
contract('Contributor app', (accounts) => {
|
||||
let kernelBase, aclBase, daoFactory, r, dao, acl, contributor;
|
||||
|
||||
const root = accounts[0];
|
||||
const member1 = accounts[1];
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
before(async () => {
|
||||
kernelBase = await getContract('Kernel').new(true); // petrify immediately
|
||||
aclBase = await getContract('ACL').new();
|
||||
daoFactory = await getContract('DAOFactory').new(kernelBase.address, aclBase.address, ZERO_ADDR);
|
||||
r = await daoFactory.newDAO(root);
|
||||
dao = await getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
|
||||
acl = await getContract('ACL').at(await dao.acl());
|
||||
|
||||
//create dao mamnager permission for coin owner
|
||||
await acl.createPermission(
|
||||
root,
|
||||
dao.address,
|
||||
await dao.APP_MANAGER_ROLE(),
|
||||
root,
|
||||
{ from: root }
|
||||
);
|
||||
|
||||
//get new app instance from DAO
|
||||
const receipt = await dao.newAppInstance(
|
||||
'0x1234',
|
||||
(await Contributor.new()).address,
|
||||
0x0,
|
||||
false,
|
||||
{ from: root }
|
||||
);
|
||||
contributor = Contributor.at(
|
||||
receipt.logs.filter(l => l.event == 'NewAppProxy')[0].args.proxy
|
||||
);
|
||||
|
||||
//apps id
|
||||
let appsId = [];
|
||||
appsId[0] = namehash("kredits-contribution");
|
||||
appsId[1] = namehash("kredits-contributor");
|
||||
appsId[2] = namehash("kredits-proposal");
|
||||
appsId[3] = namehash("kredits-token");
|
||||
|
||||
//init contributor (app)
|
||||
await contributor.initialize(root, appsId);
|
||||
|
||||
//create manage contributors role
|
||||
await acl.createPermission(
|
||||
root,
|
||||
contributor.address,
|
||||
await contributor.MANAGE_CONTRIBUTORS_ROLE(),
|
||||
root,
|
||||
{ from: root }
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
describe("Owner default permissions", async () => {
|
||||
it('check owner is contributors manager', async () => {
|
||||
let manageContributorPermission = await acl.hasPermission(root, contributor.address, await contributor.MANAGE_CONTRIBUTORS_ROLE());
|
||||
// eslint-disable-next-line no-undef
|
||||
assert.equal(manageContributorPermission, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Add contributor", async () => {
|
||||
let account = root;
|
||||
let hashDigest = '0x0';
|
||||
let hashFunction = 0;
|
||||
let hashSize = 0;
|
||||
|
||||
it("should revert when add contributor from an address that does not have permission", async () => {
|
||||
return assertRevert(async () => {
|
||||
await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1});
|
||||
'sender does not have permission';
|
||||
});
|
||||
});
|
||||
|
||||
it('add contributor', async () => {
|
||||
let contributorCount = await contributor.coreContributorsCount();
|
||||
await contributor.addContributor(account, hashDigest, hashFunction, hashSize);
|
||||
// eslint-disable-next-line no-undef
|
||||
assert.equal(await contributor.addressExists(account), true);
|
||||
let contributorCountAfter = await contributor.coreContributorsCount();
|
||||
// eslint-disable-next-line no-undef
|
||||
assert.equal(await contributorCountAfter.toNumber(), parseInt(contributorCount)+1);
|
||||
});
|
||||
|
||||
it("should revert when add contributor with an address that already exist", async () => {
|
||||
return assertRevert(async () => {
|
||||
await contributor.addContributor(account, hashDigest, hashFunction, hashSize, { from: member1});
|
||||
'address already exist';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Update contributor", async () => {
|
||||
let id;
|
||||
let oldAccount;
|
||||
let newAccount;
|
||||
let hashDigest;
|
||||
let hashFunction;
|
||||
let hashSize;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
before(async () => {
|
||||
id = await contributor.coreContributorsCount();
|
||||
oldAccount = root;
|
||||
newAccount = member1;
|
||||
hashDigest = '0x1000000000000000000000000000000000000000000000000000000000000000';
|
||||
hashFunction = 1;
|
||||
hashSize = 1;
|
||||
});
|
||||
|
||||
it('update contributor account', async () => {
|
||||
await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount);
|
||||
let contributorId = await contributor.getContributorIdByAddress(oldAccount);
|
||||
// eslint-disable-next-line no-undef
|
||||
assert.equal(contributorId.toNumber(), 0);
|
||||
});
|
||||
|
||||
it("should revert when update contributor account from address that does not have permission", async () => {
|
||||
return assertRevert(async () => {
|
||||
await contributor.updateContributorAccount(id.toNumber(), oldAccount, newAccount, {from: member1});
|
||||
'sender does not have permission';
|
||||
});
|
||||
});
|
||||
|
||||
it("should revert when update contributor account that does not exist", async () => {
|
||||
return assertRevert(async () => {
|
||||
await contributor.updateContributorAccount(id.toNumber(), accounts[3], newAccount);
|
||||
'contributor does not exist';
|
||||
});
|
||||
});
|
||||
|
||||
it("should revert when update contributor account with address(0)", async () => {
|
||||
return assertRevert(async () => {
|
||||
await contributor.updateContributorAccount(id.toNumber(), oldAccount, ZERO_ADDR);
|
||||
'contributor does not exist';
|
||||
});
|
||||
});
|
||||
|
||||
it('update contributor profile hash', async () => {
|
||||
await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize);
|
||||
let contributorProfile = await contributor.contributors(id.toNumber());
|
||||
assert.equal(hashDigest, contributorProfile[1]); // eslint-disable-line no-undef
|
||||
assert.equal(hashFunction, contributorProfile[2].toNumber()); // eslint-disable-line no-undef
|
||||
assert.equal(hashSize, contributorProfile[3].toNumber()); // eslint-disable-line no-undef
|
||||
});
|
||||
|
||||
it("should revert when update contributor profile hash from address that does not have permission", async () => {
|
||||
return assertRevert(async () => {
|
||||
await contributor.updateContributorProfileHash(id.toNumber(), hashDigest, hashFunction, hashSize, {from: member1});
|
||||
'sender does not have permission';
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -1 +0,0 @@
|
||||
module.exports = require("../../truffle.js");
|
@ -1 +0,0 @@
|
||||
# Kredits Proposal
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"roles": [
|
||||
{
|
||||
"name": "Add proposal",
|
||||
"id": "ADD_PROPOSAL_ROLE",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Vote proposals",
|
||||
"id": "VOTE_PROPOSAL_ROLE",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-proposal.open.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
"appName": "kredits-proposal.open.aragonpm.eth",
|
||||
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||
"network": "rinkeby"
|
||||
},
|
||||
"production": {
|
||||
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||
"appName": "proposal.open.aragonpm.eth",
|
||||
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||
"network": "mainnet"
|
||||
}
|
||||
},
|
||||
"path": "contracts/Proposal.sol"
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||
|
||||
interface IContributor {
|
||||
function getContributorAddressById(uint32 contributorId) public view returns (address);
|
||||
function getContributorIdByAddress(address contributorAccount) public view returns (uint32);
|
||||
function exists(uint32 contributorId) public view returns (bool);
|
||||
}
|
||||
|
||||
interface IContribution {
|
||||
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public;
|
||||
}
|
||||
|
||||
contract Proposal is AragonApp {
|
||||
|
||||
bytes32 public constant ADD_PROPOSAL_ROLE = keccak256("ADD_PROPOSAL_ROLE");
|
||||
bytes32 public constant VOTE_PROPOSAL_ROLE = keccak256("VOTE_PROPOSAL_ROLE");
|
||||
|
||||
bytes32 public constant KERNEL_APP_ADDR_NAMESPACE = 0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb;
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
|
||||
struct Proposal {
|
||||
address creatorAccount;
|
||||
uint32 contributorId;
|
||||
uint16 votesCount;
|
||||
uint16 votesNeeded;
|
||||
uint32 amount;
|
||||
bool executed;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
uint32[] voterIds;
|
||||
mapping (uint32 => bool) votes;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
mapping(uint32 => Proposal) public proposals;
|
||||
uint32 public proposalsCount;
|
||||
|
||||
event ProposalCreated(uint32 id, address creatorAccount, uint32 contributorId, uint32 amount);
|
||||
|
||||
event ProposalVoted(uint32 id, uint32 voterId, uint16 totalVotes);
|
||||
event ProposalExecuted(uint32 id, uint32 contributorId, uint32 amount);
|
||||
|
||||
function initialize(bytes32[5] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
initialized();
|
||||
}
|
||||
|
||||
function getContract(uint8 appId) public view returns (address) {
|
||||
IKernel k = IKernel(kernel());
|
||||
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[appId]);
|
||||
}
|
||||
|
||||
function addProposal(uint32 contributorId, uint32 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
|
||||
require(IContributor(getContract(uint8(Apps.Contributor))).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
|
||||
|
||||
uint32 proposalId = proposalsCount + 1;
|
||||
uint16 _votesNeeded = 1; //contributorsContract().coreContributorsCount() / 100 * 75;
|
||||
|
||||
Proposal storage p = proposals[proposalId];
|
||||
p.creatorAccount = msg.sender;
|
||||
p.contributorId = contributorId;
|
||||
p.amount = amount;
|
||||
p.hashDigest = hashDigest;
|
||||
p.hashFunction = hashFunction;
|
||||
p.hashSize = hashSize;
|
||||
p.votesCount = 0;
|
||||
p.votesNeeded = _votesNeeded;
|
||||
p.exists = true;
|
||||
|
||||
proposalsCount++;
|
||||
emit ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
|
||||
}
|
||||
|
||||
function getProposal(uint32 proposalId) public view returns (uint32 id, address creatorAccount, uint32 contributorId, uint16 votesCount, uint16 votesNeeded, uint32 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint32[] voterIds, bool exists) {
|
||||
id = proposalId;
|
||||
Proposal storage p = proposals[id];
|
||||
return (
|
||||
id,
|
||||
p.creatorAccount,
|
||||
p.contributorId,
|
||||
p.votesCount,
|
||||
p.votesNeeded,
|
||||
p.amount,
|
||||
p.executed,
|
||||
p.hashDigest,
|
||||
p.hashFunction,
|
||||
p.hashSize,
|
||||
p.voterIds,
|
||||
p.exists
|
||||
);
|
||||
}
|
||||
|
||||
function vote(uint32 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
Proposal storage p = proposals[proposalId];
|
||||
require(!p.executed, 'ALREADY_EXECUTED');
|
||||
uint32 voterId = IContributor(getContract(uint8(Apps.Contributor))).getContributorIdByAddress(msg.sender);
|
||||
require(p.votes[voterId] != true, 'ALREADY_VOTED');
|
||||
p.voterIds.push(voterId);
|
||||
p.votes[voterId] = true;
|
||||
|
||||
p.votesCount++;
|
||||
if (p.votesCount >= p.votesNeeded) {
|
||||
executeProposal(proposalId);
|
||||
}
|
||||
emit ProposalVoted(proposalId, voterId, p.votesCount);
|
||||
}
|
||||
|
||||
function batchVote(uint32[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||
for (uint32 i = 0; i < _proposalIds.length; i++) {
|
||||
vote(_proposalIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function executeProposal(uint32 proposalId) private {
|
||||
Proposal storage p = proposals[proposalId];
|
||||
require(!p.executed, 'ALREADY_EXECUTED');
|
||||
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
|
||||
|
||||
p.executed = true;
|
||||
IContribution(getContract(uint8(Apps.Contribution))).add(p.amount, p.contributorId, p.hashDigest, p.hashFunction, p.hashSize);
|
||||
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
constructor() 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);
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "Proposal",
|
||||
"description": "Kredits Proposal app"
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Migrations = artifacts.require('./Migrations.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Migrations)
|
||||
}
|
7900
apps/proposal/package-lock.json
generated
7900
apps/proposal/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "kredits-proposal",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@aragon/os": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aragon/test-helpers": "^2.1.0",
|
||||
"eth-gas-reporter": "^0.2.17",
|
||||
"ganache-cli": "^6.9.1",
|
||||
"solidity-coverage": "^0.5.11"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
"build:script": "",
|
||||
"build": "",
|
||||
"publish:patch": "aragon apm publish patch",
|
||||
"publish:minor": "aragon apm publish minor",
|
||||
"publish:major": "aragon apm publish major",
|
||||
"versions": "aragon apm versions",
|
||||
"test": "truffle test"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// const Proposal = artifacts.require('Proposal.sol');
|
||||
|
||||
contract('Proposal', (_accounts) => {
|
||||
it('should be tested');
|
||||
});
|
@ -1 +0,0 @@
|
||||
module.exports = require("../../truffle.js");
|
1
apps/reimbursement/.gitattributes
vendored
1
apps/reimbursement/.gitattributes
vendored
@ -1 +0,0 @@
|
||||
*.sol linguist-language=Solidity
|
7
apps/reimbursement/.gitignore
vendored
7
apps/reimbursement/.gitignore
vendored
@ -1,7 +0,0 @@
|
||||
node_modules
|
||||
artifacts
|
||||
.cache
|
||||
cache
|
||||
dist
|
||||
ipfs.cmd
|
||||
package-lock.json
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"roles": [
|
||||
{
|
||||
"name": "Add reimursements",
|
||||
"id": "ADD_REIMBURSEMENT_ROLE",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"name": "Veto reimbursement",
|
||||
"id": "VETO_REIMBURSEMENT_ROLE",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-reimbursement.open.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
"appName": "kredits-reimbursement.open.aragonpm.eth",
|
||||
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||
"network": "rinkeby"
|
||||
},
|
||||
"production": {
|
||||
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||
"appName": "kredits-reimbursement.aragonpm.eth",
|
||||
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||
"network": "mainnet"
|
||||
}
|
||||
},
|
||||
"appName": "kredits-reimbursement.aragonpm.eth",
|
||||
"path": "contracts/Reimbursement.sol"
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
const { usePlugin } = require('@nomiclabs/buidler/config')
|
||||
const hooks = require('./scripts/buidler-hooks')
|
||||
|
||||
usePlugin('@aragon/buidler-aragon')
|
||||
|
||||
module.exports = {
|
||||
// Default Buidler configurations. Read more about it at https://buidler.dev/config/
|
||||
defaultNetwork: 'localhost',
|
||||
networks: {
|
||||
localhost: {
|
||||
url: 'http://localhost:8545',
|
||||
},
|
||||
},
|
||||
solc: {
|
||||
version: '0.4.24',
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 10000,
|
||||
},
|
||||
},
|
||||
// Etherscan plugin configuration. Learn more at https://github.com/nomiclabs/buidler/tree/master/packages/buidler-etherscan
|
||||
etherscan: {
|
||||
apiKey: '', // API Key for smart contract verification. Get yours at https://etherscan.io/apis
|
||||
},
|
||||
// Aragon plugin configuration
|
||||
aragon: {
|
||||
appServePort: 8001,
|
||||
clientServePort: 3000,
|
||||
appSrcPath: 'app/',
|
||||
appBuildOutputPath: 'dist/',
|
||||
appName: 'expenses',
|
||||
hooks, // Path to script hooks
|
||||
},
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||
|
||||
contract Reimbursement is AragonApp {
|
||||
bytes32 public constant ADD_REIMBURSEMENT_ROLE = keccak256("ADD_REIMBURSEMENT_ROLE");
|
||||
bytes32 public constant VETO_REIMBURSEMENT_ROLE = keccak256("VETO_REIMBURSEMENT_ROLE");
|
||||
// bytes32 public constant MANAGE_APPS_ROLE = keccak256("MANAGE_APPS_ROLE");
|
||||
|
||||
struct ReimbursementData {
|
||||
uint32 recipientId;
|
||||
uint256 amount;
|
||||
address token;
|
||||
bytes32 hashDigest;
|
||||
uint8 hashFunction;
|
||||
uint8 hashSize;
|
||||
uint256 confirmedAtBlock;
|
||||
bool vetoed;
|
||||
bool exists;
|
||||
}
|
||||
|
||||
mapping(uint32 => ReimbursementData) public reimbursements;
|
||||
uint32 public reimbursementsCount;
|
||||
|
||||
uint32 public blocksToWait;
|
||||
|
||||
event ReimbursementAdded(uint32 id, address indexed addedByAccount, uint256 amount);
|
||||
event ReimbursementVetoed(uint32 id, address vetoedByAccount);
|
||||
|
||||
function initialize() public onlyInit {
|
||||
blocksToWait = 40320; // 7 days; 15 seconds block time
|
||||
initialized();
|
||||
}
|
||||
|
||||
// function setApps() public isInitialized auth(MANAGE_APPS_ROLE) {
|
||||
// }
|
||||
|
||||
function totalAmount(bool confirmedOnly) public view returns (uint256 amount) {
|
||||
for (uint32 i = 1; i <= reimbursementsCount; i++) {
|
||||
ReimbursementData memory r = reimbursements[i];
|
||||
if (!r.vetoed && (block.number >= r.confirmedAtBlock || !confirmedOnly)) {
|
||||
amount += r.amount; // should use safemath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get(uint32 reimbursementId) public view returns (uint32 id, uint32 recipientId, uint256 amount, address token, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
|
||||
id = reimbursementId;
|
||||
ReimbursementData storage r = reimbursements[id];
|
||||
return (
|
||||
id,
|
||||
r.recipientId,
|
||||
r.amount,
|
||||
r.token,
|
||||
r.hashDigest,
|
||||
r.hashFunction,
|
||||
r.hashSize,
|
||||
r.confirmedAtBlock,
|
||||
r.exists,
|
||||
r.vetoed
|
||||
);
|
||||
}
|
||||
|
||||
function add(uint256 amount, address token, uint32 recipientId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_REIMBURSEMENT_ROLE) {
|
||||
uint32 reimbursementId = reimbursementsCount + 1;
|
||||
ReimbursementData storage r = reimbursements[reimbursementId];
|
||||
r.exists = true;
|
||||
r.amount = amount;
|
||||
r.token = token;
|
||||
r.recipientId = recipientId;
|
||||
r.hashDigest = hashDigest;
|
||||
r.hashFunction = hashFunction;
|
||||
r.hashSize = hashSize;
|
||||
r.confirmedAtBlock = block.number + blocksToWait;
|
||||
|
||||
reimbursementsCount++;
|
||||
|
||||
emit ReimbursementAdded(reimbursementId, msg.sender, amount);
|
||||
}
|
||||
|
||||
function veto(uint32 reimbursementId) public isInitialized auth(VETO_REIMBURSEMENT_ROLE) {
|
||||
ReimbursementData storage r = reimbursements[reimbursementId];
|
||||
require(r.exists, 'NOT_FOUND');
|
||||
require(block.number < r.confirmedAtBlock, 'VETO_PERIOD_ENDED');
|
||||
r.vetoed = true;
|
||||
|
||||
emit ReimbursementVetoed(reimbursementId, msg.sender);
|
||||
}
|
||||
|
||||
function exists(uint32 reimbursementId) public view returns (bool) {
|
||||
return reimbursements[reimbursementId].exists;
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "kredits Reimbursement",
|
||||
"author": "Placeholder-author",
|
||||
"description": "An application for Aragon",
|
||||
"details_url": "/meta/details.md",
|
||||
"source_url": "https://<placeholder-repository-url>",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/meta/icon.svg",
|
||||
"sizes": "56x56"
|
||||
}
|
||||
],
|
||||
"screenshots": [{ "src": "/meta/screenshot-1.png" }],
|
||||
"start_url": "/index.html",
|
||||
"script": "/script.js"
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "kredits-reimbursement",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
"build:script": "",
|
||||
"build": "",
|
||||
"publish:patch": "aragon apm publish patch",
|
||||
"publish:minor": "aragon apm publish minor",
|
||||
"publish:major": "aragon apm publish major",
|
||||
"versions": "aragon apm versions",
|
||||
"test": "truffle test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aragon/os": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aragon/test-helpers": "^2.1.0",
|
||||
"eth-gas-reporter": "^0.2.17",
|
||||
"ganache-cli": "^6.9.1",
|
||||
"solidity-coverage": "^0.5.11"
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
|
||||
const { hash } = require('eth-ens-namehash')
|
||||
const { getEventArgument } = require('@aragon/contract-test-helpers/events')
|
||||
const Kernel = artifacts.require('@aragon/os/build/contracts/kernel/Kernel')
|
||||
const ACL = artifacts.require('@aragon/os/build/contracts/acl/ACL')
|
||||
const EVMScriptRegistryFactory = artifacts.require(
|
||||
'@aragon/os/build/contracts/factory/EVMScriptRegistryFactory'
|
||||
)
|
||||
const DAOFactory = artifacts.require(
|
||||
'@aragon/os/build/contracts/factory/DAOFactory'
|
||||
)
|
||||
|
||||
const newDao = async (rootAccount) => {
|
||||
// Deploy a DAOFactory.
|
||||
const kernelBase = await Kernel.new(true)
|
||||
const aclBase = await ACL.new()
|
||||
const registryFactory = await EVMScriptRegistryFactory.new()
|
||||
const daoFactory = await DAOFactory.new(
|
||||
kernelBase.address,
|
||||
aclBase.address,
|
||||
registryFactory.address
|
||||
)
|
||||
|
||||
// Create a DAO instance.
|
||||
const daoReceipt = await daoFactory.newDAO(rootAccount)
|
||||
const dao = await Kernel.at(getEventArgument(daoReceipt, 'DeployDAO', 'dao'))
|
||||
|
||||
// Grant the rootAccount address permission to install apps in the DAO.
|
||||
const acl = await ACL.at(await dao.acl())
|
||||
const APP_MANAGER_ROLE = await kernelBase.APP_MANAGER_ROLE()
|
||||
await acl.createPermission(
|
||||
rootAccount,
|
||||
dao.address,
|
||||
APP_MANAGER_ROLE,
|
||||
rootAccount,
|
||||
{ from: rootAccount }
|
||||
)
|
||||
|
||||
return { dao, acl }
|
||||
}
|
||||
|
||||
const newApp = async (dao, appName, baseAppAddress, rootAccount) => {
|
||||
const receipt = await dao.newAppInstance(
|
||||
hash(`${appName}.aragonpm.test`), // appId - Unique identifier for each app installed in the DAO; can be any bytes32 string in the tests.
|
||||
baseAppAddress, // appBase - Location of the app's base implementation.
|
||||
'0x', // initializePayload - Used to instantiate and initialize the proxy in the same call (if given a non-empty bytes string).
|
||||
false, // setDefault - Whether the app proxy is the default proxy.
|
||||
{ from: rootAccount }
|
||||
)
|
||||
|
||||
// Find the deployed proxy address in the tx logs.
|
||||
const logs = receipt.logs
|
||||
const log = logs.find((l) => l.event === 'NewAppProxy')
|
||||
const proxyAddress = log.args.proxy
|
||||
|
||||
return proxyAddress
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
newDao,
|
||||
newApp,
|
||||
}
|
||||
|
||||
*/
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
|
||||
const ANY_ADDRESS = '0xffffffffffffffffffffffffffffffffffffffff'
|
||||
|
||||
const setOpenPermission = async (acl, appAddress, role, rootAddress) => {
|
||||
// Note: Setting a permission to 0xffffffffffffffffffffffffffffffffffffffff
|
||||
// is interpreted by aragonOS as allowing the role for any address.
|
||||
await acl.createPermission(
|
||||
ANY_ADDRESS, // entity (who?) - The entity or address that will have the permission.
|
||||
appAddress, // app (where?) - The app that holds the role involved in this permission.
|
||||
role, // role (what?) - The particular role that the entity is being assigned to in this permission.
|
||||
rootAddress, // manager - Can grant/revoke further permissions for this role.
|
||||
{ from: rootAddress}
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setOpenPermission
|
||||
}
|
||||
|
||||
*/
|
@ -1 +0,0 @@
|
||||
module.exports = require("../../truffle.js");
|
File diff suppressed because it is too large
Load Diff
4
apps/token/.gitignore
vendored
4
apps/token/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
build
|
||||
.cache
|
||||
dist
|
@ -1,14 +0,0 @@
|
||||
# Git files
|
||||
.gitignore
|
||||
|
||||
# Build files
|
||||
.cache
|
||||
node_modules
|
||||
build
|
||||
|
||||
# Lock files
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Others
|
||||
test
|
@ -1 +0,0 @@
|
||||
# Kredits Token
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"roles": [
|
||||
{
|
||||
"name": "Mint token",
|
||||
"id": "MINT_TOKEN_ROLE",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"environments": {
|
||||
"default": {
|
||||
"network": "development",
|
||||
"appName": "kredits-token.open.aragonpm.eth"
|
||||
},
|
||||
"rinkeby": {
|
||||
"registry": "0x98df287b6c145399aaa709692c8d308357bc085d",
|
||||
"appName": "kredits-token.open.aragonpm.eth",
|
||||
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||
"network": "rinkeby"
|
||||
},
|
||||
"mainnet": {
|
||||
"registry": "0x314159265dd8dbb310642f98f50c066173c1259b",
|
||||
"appName": "kredits-token.open.aragonpm.eth",
|
||||
"wsRPC": "wss://mainnet.eth.aragon.network/ws",
|
||||
"network": "mainnet"
|
||||
}
|
||||
},
|
||||
"path": "contracts/Token.sol"
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/lib/math/SafeMath.sol";
|
||||
|
||||
/**
|
||||
* beause ERC20.sol conflicts with the aragon ERC20.sol this is copied and modified from:
|
||||
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/token/ERC20/ERC20.sol
|
||||
* @title Standard ERC20 token
|
||||
*
|
||||
* @dev Implementation of the basic standard token.
|
||||
* https://eips.ethereum.org/EIPS/eip-20
|
||||
* Originally based on code by FirstBlood:
|
||||
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
|
||||
*
|
||||
* This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
|
||||
* all accounts just by listening to said events. Note that this isn't required by the specification, and other
|
||||
* compliant implementations may not do it.
|
||||
*/
|
||||
contract ERC20Token {
|
||||
using SafeMath for uint256;
|
||||
|
||||
mapping (address => uint256) public _balances;
|
||||
|
||||
mapping (address => mapping (address => uint256)) private _allowed;
|
||||
|
||||
uint256 public _totalSupply;
|
||||
|
||||
string public name;
|
||||
string public symbol;
|
||||
uint8 public decimals;
|
||||
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
|
||||
/**
|
||||
* @dev Total number of tokens in existence
|
||||
*/
|
||||
function totalSupply() public view returns (uint256) {
|
||||
return _totalSupply;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Gets the balance of the specified address.
|
||||
* @param owner The address to query the balance of.
|
||||
* @return A uint256 representing the amount owned by the passed address.
|
||||
*/
|
||||
function balanceOf(address owner) public view returns (uint256) {
|
||||
return _balances[owner];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to check the amount of tokens that an owner allowed to a spender.
|
||||
* @param owner address The address which owns the funds.
|
||||
* @param spender address The address which will spend the funds.
|
||||
* @return A uint256 specifying the amount of tokens still available for the spender.
|
||||
*/
|
||||
function allowance(address owner, address spender) public view returns (uint256) {
|
||||
return _allowed[owner][spender];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfer token to a specified address
|
||||
* @param to The address to transfer to.
|
||||
* @param value The amount to be transferred.
|
||||
*/
|
||||
function transfer(address to, uint256 value) public returns (bool) {
|
||||
_transfer(msg.sender, to, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
|
||||
* Beware that changing an allowance with this method brings the risk that someone may use both the old
|
||||
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
|
||||
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
|
||||
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
||||
* @param spender The address which will spend the funds.
|
||||
* @param value The amount of tokens to be spent.
|
||||
*/
|
||||
function approve(address spender, uint256 value) public returns (bool) {
|
||||
_approve(msg.sender, spender, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfer tokens from one address to another.
|
||||
* Note that while this function emits an Approval event, this is not required as per the specification,
|
||||
* and other compliant implementations may not emit the event.
|
||||
* @param from address The address which you want to send tokens from
|
||||
* @param to address The address which you want to transfer to
|
||||
* @param value uint256 the amount of tokens to be transferred
|
||||
*/
|
||||
function transferFrom(address from, address to, uint256 value) public returns (bool) {
|
||||
_transfer(from, to, value);
|
||||
_approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Increase the amount of tokens that an owner allowed to a spender.
|
||||
* approve should be called when _allowed[msg.sender][spender] == 0. To increment
|
||||
* allowed value is better to use this function to avoid 2 calls (and wait until
|
||||
* the first transaction is mined)
|
||||
* From MonolithDAO Token.sol
|
||||
* Emits an Approval event.
|
||||
* @param spender The address which will spend the funds.
|
||||
* @param addedValue The amount of tokens to increase the allowance by.
|
||||
*/
|
||||
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
|
||||
_approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Decrease the amount of tokens that an owner allowed to a spender.
|
||||
* approve should be called when _allowed[msg.sender][spender] == 0. To decrement
|
||||
* allowed value is better to use this function to avoid 2 calls (and wait until
|
||||
* the first transaction is mined)
|
||||
* From MonolithDAO Token.sol
|
||||
* Emits an Approval event.
|
||||
* @param spender The address which will spend the funds.
|
||||
* @param subtractedValue The amount of tokens to decrease the allowance by.
|
||||
*/
|
||||
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
|
||||
_approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfer token for a specified addresses
|
||||
* @param from The address to transfer from.
|
||||
* @param to The address to transfer to.
|
||||
* @param value The amount to be transferred.
|
||||
*/
|
||||
function _transfer(address from, address to, uint256 value) internal {
|
||||
require(to != address(0));
|
||||
|
||||
_balances[from] = _balances[from].sub(value);
|
||||
_balances[to] = _balances[to].add(value);
|
||||
emit Transfer(from, to, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Internal function that mints an amount of the token and assigns it to
|
||||
* an account. This encapsulates the modification of balances such that the
|
||||
* proper events are emitted.
|
||||
* @param account The account that will receive the created tokens.
|
||||
* @param value The amount that will be created.
|
||||
*/
|
||||
function _mint(address account, uint256 value) internal {
|
||||
require(account != address(0), 'invalid address');
|
||||
|
||||
_totalSupply = _totalSupply.add(value);
|
||||
_balances[account] = _balances[account].add(value);
|
||||
emit Transfer(address(0), account, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Approve an address to spend another addresses' tokens.
|
||||
* @param owner The address that owns the tokens.
|
||||
* @param spender The address that will spend the tokens.
|
||||
* @param value The number of tokens that can be spent.
|
||||
*/
|
||||
function _approve(address owner, address spender, uint256 value) internal {
|
||||
require(spender != address(0));
|
||||
require(owner != address(0));
|
||||
|
||||
_allowed[owner][spender] = value;
|
||||
emit Approval(owner, spender, value);
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||
import "./ERC20Token.sol";
|
||||
|
||||
contract Token is ERC20Token, AragonApp {
|
||||
bytes32 public constant MINT_TOKEN_ROLE = keccak256("MINT_TOKEN_ROLE");
|
||||
|
||||
// ensure alphabetic order
|
||||
enum Apps { Contribution, Contributor, Proposal, Reimbursement, Token }
|
||||
bytes32[5] public appIds;
|
||||
|
||||
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
|
||||
|
||||
function initialize(bytes32[5] _appIds) public onlyInit {
|
||||
appIds = _appIds;
|
||||
name = 'Kredits';
|
||||
symbol = '₭S';
|
||||
decimals = 18;
|
||||
initialized();
|
||||
}
|
||||
|
||||
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
|
||||
require(amount > 0, "INVALID_AMOUNT");
|
||||
|
||||
uint256 amountInWei = amount.mul(1 ether);
|
||||
_mint(contributorAccount, amountInWei);
|
||||
emit LogMint(contributorAccount, amount, contributionId);
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
constructor() 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);
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "@aragon/os/contracts/acl/ACL.sol";
|
||||
import "@aragon/os/contracts/kernel/Kernel.sol";
|
||||
import "@aragon/os/contracts/factory/DAOFactory.sol";
|
||||
|
||||
// You might think this file is a bit odd, but let me explain.
|
||||
// We only use for now those imported contracts in our tests, which
|
||||
// means Truffle will not compile them for us, because they are from
|
||||
// an external dependency.
|
||||
|
||||
|
||||
// solium-disable-next-line no-empty-blocks
|
||||
contract Spoof {
|
||||
// ...
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "Token",
|
||||
"description": "Kredits Token app"
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Migrations = artifacts.require('./Migrations.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Migrations)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
var Token = artifacts.require('Token.sol')
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Token)
|
||||
}
|
7900
apps/token/package-lock.json
generated
7900
apps/token/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "kredits-token",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@aragon/os": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aragon/test-helpers": "^2.1.0",
|
||||
"eth-gas-reporter": "^0.2.17",
|
||||
"ganache-cli": "^6.9.1",
|
||||
"solidity-coverage": "^0.5.11"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "npm run start:aragon:ipfs",
|
||||
"start:aragon:ipfs": "aragon run",
|
||||
"start:aragon:http": "aragon run --http localhost:8001 --http-served-from ./dist",
|
||||
"start:app": "",
|
||||
"compile": "aragon contracts compile",
|
||||
"sync-assets": "",
|
||||
"build:app": "",
|
||||
"build:script": "",
|
||||
"build": "",
|
||||
"publish:patch": "aragon apm publish patch",
|
||||
"publish:minor": "aragon apm publish minor",
|
||||
"publish:major": "aragon apm publish major",
|
||||
"versions": "aragon apm versions",
|
||||
"test": "truffle test"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
const namehash = require('ethers').utils.namehash;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const Token = artifacts.require("Token.sol");
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const getContract = name => artifacts.require(name);
|
||||
const { assertRevert } = require('@aragon/test-helpers/assertThrow');
|
||||
|
||||
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
contract('Token app', (accounts) => {
|
||||
let kernelBase, aclBase, daoFactory, dao, r, acl, token;
|
||||
|
||||
const root = accounts[0];
|
||||
const member1 = accounts[1];
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
before(async () => {
|
||||
kernelBase = await getContract('Kernel').new(true); // petrify immediately
|
||||
aclBase = await getContract('ACL').new();
|
||||
daoFactory = await getContract('DAOFactory').new(kernelBase.address, aclBase.address, ZERO_ADDR);
|
||||
r = await daoFactory.newDAO(root);
|
||||
dao = await getContract('Kernel').at(r.logs.filter(l => l.event == 'DeployDAO')[0].args.dao);
|
||||
acl = await getContract('ACL').at(await dao.acl());
|
||||
|
||||
//create dao mamnager permission for coin owner
|
||||
await acl.createPermission(
|
||||
root,
|
||||
dao.address,
|
||||
await dao.APP_MANAGER_ROLE(),
|
||||
root,
|
||||
{ from: root }
|
||||
);
|
||||
|
||||
//get new app instance from DAO
|
||||
const receipt = await dao.newAppInstance(
|
||||
'0x1234',
|
||||
(await Token.new()).address,
|
||||
0x0,
|
||||
false,
|
||||
{ from: root }
|
||||
);
|
||||
token = Token.at(
|
||||
receipt.logs.filter(l => l.event == 'NewAppProxy')[0].args.proxy
|
||||
);
|
||||
|
||||
//apps id
|
||||
let appsId = [];
|
||||
appsId[0] = namehash("kredits-contribution");
|
||||
appsId[1] = namehash("kredits-contributor");
|
||||
appsId[2] = namehash("kredits-proposal");
|
||||
appsId[3] = namehash("kredits-token");
|
||||
|
||||
//init token (app)
|
||||
await token.initialize(appsId);
|
||||
|
||||
//create token mint permission for coin owner
|
||||
await acl.createPermission(
|
||||
root,
|
||||
token.address,
|
||||
await token.MINT_TOKEN_ROLE(),
|
||||
root,
|
||||
{ from: root }
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
describe("Owner default space permissions", async () => {
|
||||
it('check owner is token issuer', async () => {
|
||||
let tokenIssuerPermission = await acl.hasPermission(root, token.address, await token.MINT_TOKEN_ROLE());
|
||||
// eslint-disable-next-line no-undef
|
||||
assert.equal(tokenIssuerPermission, true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Token issuing", async () => {
|
||||
let name = "Kredits";
|
||||
let symbol = "₭S";
|
||||
let decimals = 18;
|
||||
|
||||
it("check token properties", async () => {
|
||||
assert.equal(await token.name(), name); // eslint-disable-line no-undef
|
||||
assert.equal(await token.symbol(), symbol); // eslint-disable-line no-undef
|
||||
assert.equal(await token.decimals(), decimals); // eslint-disable-line no-undef
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Token minting", async () => {
|
||||
let tokenToMint = 250;
|
||||
let ether = 1000000000000000000;
|
||||
|
||||
it("should revert when mint tokens from an address that does not have minting permission", async () => {
|
||||
return assertRevert(async () => {
|
||||
await token.mintFor(root, tokenToMint, 1, { from: member1});
|
||||
'address does not have permission to mint tokens';
|
||||
});
|
||||
});
|
||||
|
||||
it("should revert when mint tokens to address(0)", async () => {
|
||||
return assertRevert(async () => {
|
||||
await token.mintFor(ZERO_ADDR, tokenToMint, 1, { from: root});
|
||||
'invalid contributor address';
|
||||
});
|
||||
});
|
||||
|
||||
it("should revert when mint amount of tokens equal to 0", async () => {
|
||||
return assertRevert(async () => {
|
||||
await token.mintFor(root, 0, 1, { from: root});
|
||||
'amount to mint should be greater than zero';
|
||||
});
|
||||
});
|
||||
|
||||
it("mint tokens", async () => {
|
||||
await token.mintFor(root, tokenToMint, 1, { from: root });
|
||||
let ownerBalance = await token.balanceOf(root);
|
||||
let totalSupply = await token.totalSupply();
|
||||
assert.equal(ownerBalance.toNumber(), tokenToMint*ether); // eslint-disable-line no-undef
|
||||
assert.equal(totalSupply.toNumber(), tokenToMint*ether); // eslint-disable-line no-undef
|
||||
});
|
||||
});
|
||||
});
|
@ -1 +0,0 @@
|
||||
module.exports = require("../../truffle.js");
|
Loading…
x
Reference in New Issue
Block a user