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