commit
f40cc1d8ff
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
|||||||
build
|
build
|
||||||
|
flattened_contracts
|
||||||
node_modules
|
node_modules
|
||||||
|
**/node_modules
|
||||||
.ganache-db
|
.ganache-db
|
||||||
.tm_properties
|
.tm_properties
|
||||||
|
193
README.md
193
README.md
@ -2,92 +2,82 @@
|
|||||||
|
|
||||||
# Kredits Contracts
|
# Kredits Contracts
|
||||||
|
|
||||||
This repository contains the Solidity smart contracts and JavaScript API
|
This repository contains the Solidity smart contracts organized as [Aragon](https://hack.aragon.org/)
|
||||||
wrapper for [Kosmos Kredits](https://wiki.kosmos.org/Kredits).
|
apps and JavaScript API wrapper for [Kosmos Kredits](https://wiki.kosmos.org/Kredits).
|
||||||
|
|
||||||
It uses the [Truffle framework](http://truffleframework.com/) for some things.
|
It is based on [aragonOS](https://hack.aragon.org/docs/aragonos-intro.html) and
|
||||||
|
follows the aragonOS conventions.
|
||||||
|
Aragon itself uses the [Truffle framework](http://truffleframework.com/) for some things.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
$ npm install
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
All requirements are defined in `package.json`.
|
All requirements are defined in `package.json`.
|
||||||
|
|
||||||
Those can be installed globally for convenience:
|
$ npm install
|
||||||
|
|
||||||
* [truffle framework](http://truffleframework.com): `npm install -g truffle`
|
Each of the aragon apps are separate packages:
|
||||||
* [ganache](http://truffleframework.com/ganache): `npm install -g ganache-cli`
|
|
||||||
|
|
||||||
We use following solidity contract libraries:
|
$ cd apps/[app]
|
||||||
|
$ npm install
|
||||||
|
|
||||||
* [Open Zeppelin](https://github.com/OpenZeppelin/zeppelin-solidity)
|
or use the bootstrap command (see below)
|
||||||
|
|
||||||
|
### Local development chain
|
||||||
|
|
||||||
For local development it is recommended to use
|
For local development it is recommended to use
|
||||||
[ganache-cli](https://github.com/trufflesuite/ganache-cli) (or the [ganache
|
[ganache](http://truffleframework.com/ganache/) to run a local development
|
||||||
GUI](http://truffleframework.com/ganache/) to run a local development chain.
|
chain. Using the ganache simulator no full Ethereum node is required.
|
||||||
Using the ganache simulator no full Ethereum node is required.
|
|
||||||
|
|
||||||
We default to:
|
We use the default aragon-cli devchain command to confgure and run a local
|
||||||
|
development ganache.
|
||||||
|
|
||||||
* port 7545 for development to not get in conflict with the default Ethereum
|
$ npm run devchain (or aragon devchain --port 7545)
|
||||||
RPC port.
|
|
||||||
* network ID 100 to stay on the same network id
|
|
||||||
* store ganache data in .ganache-db to presist the chain data across restarts
|
|
||||||
* use a fixed Mnemonic code to get the same accounts across restarts
|
|
||||||
|
|
||||||
Have a look at `ganache-cli` for more configuration options.
|
To clear/reset the chain use:
|
||||||
|
|
||||||
Run your ganache simulator before using Kredits locally:
|
$ npm run devchain -- --reset (or aragon devchain --port 7545 --reset)
|
||||||
|
|
||||||
$ npm run ganache (which is: ganache-cli -p 7545 -i 100 --db=./.ganache-db -m kredits)
|
We default to port 7545 for development to not get in conflict with the default
|
||||||
|
Ethereum RPC port.
|
||||||
|
|
||||||
### Truffle console
|
### Bootstrap
|
||||||
|
|
||||||
Truffle comes with a simple REPL to interact with the Smart Contracts. Have a
|
1. Run an Ethereum node and ipfs
|
||||||
look at the [documentation
|
|
||||||
here](http://truffleframework.com/docs/getting_started/console)
|
|
||||||
|
|
||||||
NOTE: There are promisses, have a look at the examples:
|
$ npm run devchain
|
||||||
|
$ ipfs daemon
|
||||||
|
|
||||||
```javascript
|
2. Deploy each app to the devchain
|
||||||
Token.deployed().then(function(token) {
|
|
||||||
token.totalSupply.call().then(function(value) {
|
|
||||||
console.log(value.toString());
|
|
||||||
})
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Also please be aware of the differences between web3.js 0.2x.x and
|
$ npm run deploy:apps
|
||||||
[1.x.x](https://web3js.readthedocs.io/en/1.0/) - [web3
|
|
||||||
repo](https://github.com/ethereum/web3.js/)
|
|
||||||
|
|
||||||
## Contract Deployment
|
3. Deploy a new KreditsKit and create a new DAO with the latest app versions
|
||||||
|
|
||||||
Truffle uses migration scripts to deploy contract to various networks. Have a
|
$ npm run deploy:kit
|
||||||
look at the `migrations` folder for those. The Ethereum nodes for the
|
$ npm run deploy:dao
|
||||||
different networks need to be configured in `truffle.js`.
|
|
||||||
|
|
||||||
Run the truffle migration scripts:
|
4. Execute seeds to create demo contributors, contributons, etc. (optional)
|
||||||
|
|
||||||
$ truffle deploy
|
$ npm run seeds
|
||||||
$ truffle deploy --network=<network config from truffle.js>
|
|
||||||
|
|
||||||
Truffle keeps track of already executed migration scripts. To reset the
|
**Step 2-4 is also summarized in `npm run bootstrap`**
|
||||||
migration use the `--reset` option
|
|
||||||
|
|
||||||
$ truffle migrate --reset
|
## Contract architecture
|
||||||
|
|
||||||
Migration scripts can also be run from within `truffle console` or `truffle
|
Contracts are organized in independent apps (see `/apps`) and are developed
|
||||||
develop`
|
and deployed independently. Each app has a version and can be "installed"
|
||||||
|
on the Kredits DAO independently.
|
||||||
|
|
||||||
To initially bootstrap a local development chain in ganache you can use the
|

|
||||||
bootstrap script:
|
|
||||||
|
A DAO can be deployed using the `scripts/deploy-kit.js` script or with the
|
||||||
|
`npm run deploy:dao` command. This deploys a new Kredits DAO, installs
|
||||||
|
the latest app versions and sets the required permissions.
|
||||||
|
|
||||||
|
See each app in `/apps/*` for details.
|
||||||
|
|
||||||
$ npm run bootstrap
|
|
||||||
|
|
||||||
## Helper scripts
|
## Helper scripts
|
||||||
|
|
||||||
@ -110,18 +100,17 @@ instance.
|
|||||||
|
|
||||||
$ truffle exec scripts/repl.js
|
$ truffle exec scripts/repl.js
|
||||||
|
|
||||||
### add-contributor.js
|
### add-{contributor, contribution, proposal}.js
|
||||||
|
|
||||||
Adds a new core contributor, creates a proposal for the new contributor and
|
Script to add a new entries to the contracts using the JS wrapper
|
||||||
votes for that one.
|
|
||||||
|
|
||||||
$ truffle exec scripts/add-contributor.js
|
$ truffle exec scripts/add-{contributor, contribution, proposal}.js
|
||||||
|
|
||||||
### add-proposal.js
|
### list-{contributor, contribution, proposal}.js
|
||||||
|
|
||||||
Adds a new proposal for an existing contributor
|
List contract entries
|
||||||
|
|
||||||
$ truffle exec scripts/add-proposal.js
|
$ truffle exec scripts/list-{contributor, contribution, proposal}.js
|
||||||
|
|
||||||
### send-funds.js
|
### send-funds.js
|
||||||
|
|
||||||
@ -131,48 +120,70 @@ metamask account.
|
|||||||
$ truffle exec scripts/send-funds.js
|
$ truffle exec scripts/send-funds.js
|
||||||
|
|
||||||
### seeds.js
|
### seeds.js
|
||||||
|
|
||||||
Run seeds defined in `config/seeds.js`.
|
Run seeds defined in `config/seeds.js`.
|
||||||
|
|
||||||
$ truffle exec scripts/seeds.js
|
$ truffle exec scripts/seeds.js
|
||||||
or
|
or
|
||||||
$ npm run seeds
|
$ npm run seeds
|
||||||
|
|
||||||
|
### current-address.js
|
||||||
|
|
||||||
|
Prints all known DAO addresses and the DAO address for the current network
|
||||||
|
|
||||||
|
$ truffle exec scripts/current-address.js
|
||||||
|
or
|
||||||
|
$ npm run dao:address
|
||||||
|
|
||||||
|
### deploy-kit.js
|
||||||
|
|
||||||
|
Deploys a new KreditsKit that allows to create a new DAO
|
||||||
|
|
||||||
|
$ truffle exec script/deploy-kit.js
|
||||||
|
or
|
||||||
|
$ npm run deploy:kit
|
||||||
|
|
||||||
|
`ENS` address is required as environment variable.
|
||||||
|
`DAO_FACTORY` can optionally be set as environment variable. (see aragon)
|
||||||
|
|
||||||
|
### new-dao.js
|
||||||
|
|
||||||
|
Creates and configures a new DAO instance.
|
||||||
|
|
||||||
|
$ truffle exec script/new-dao.js
|
||||||
|
or
|
||||||
|
$ npm run deploy:dao
|
||||||
|
|
||||||
|
KreditsKit address is load from `lib/addresses/KreditsKit.json` or can be
|
||||||
|
configured through the `KREDITS_KIT` environment variable.
|
||||||
|
|
||||||
|
### deploy-apps.sh
|
||||||
|
|
||||||
|
Runs `npm install` for each app and publishes a new version.
|
||||||
|
|
||||||
|
$ ./scripts/deploy-apps.sh
|
||||||
|
or
|
||||||
|
$ npm run deploy:apps
|
||||||
|
|
||||||
|
|
||||||
|
## ACL / Permissions
|
||||||
|
|
||||||
|
|
||||||
## Upgradeable contracts
|
## Upgradeable contracts
|
||||||
|
|
||||||
Some of the contracts use upgradability ideas from
|
We use aragonOS for upgradeablity of the different contracts.
|
||||||
[zeppelinos](https://github.com/zeppelinos/labs) (see `contracts/upgradable`).
|
Refer to the [aragonOS upgradeablity documentation](https://hack.aragon.org/docs/upgradeability-intro)
|
||||||
|
for more details.
|
||||||
The basic idea is to have a Registry contract that knows about the current
|
|
||||||
implementations and a Proxy contract that uses `delegatecall` to call the
|
|
||||||
current implementation. That means the Proxy contract holds the storage and
|
|
||||||
the address of that one does not change but the actuall implemenation is
|
|
||||||
managed through the Registry.
|
|
||||||
|
|
||||||
To deploy a new version a new contract is deployed then the version is
|
|
||||||
registered (`addVersion()`) in the Registry and on the Proxy contract is
|
|
||||||
"upgraded" (`upgrade()`) to the new version.
|
|
||||||
|
|
||||||
The Registry knows about all the different contracts and implementations.
|
|
||||||
Versions are stored as uint and automatically incremented for every added
|
|
||||||
implementation.
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
Deployment is best done using the truffle deployer.
|
1. Setup (see #Bootstrap)
|
||||||
|
1. Deploy each contract/apps (see `/apps/*`)
|
||||||
1. Setup
|
2. Create a new DAO (see scripts/deploy-kit.js)
|
||||||
1. Deploy the Registry
|
|
||||||
2. Deploy the contract
|
|
||||||
3. Register the contract at the Registry:
|
|
||||||
`registry.addVersion('Token', Token.address)`
|
|
||||||
4. Create the Proxy:
|
|
||||||
`registry.createProxy('Token', 1)`
|
|
||||||
2. Update
|
2. Update
|
||||||
1. Deploy a new Version of the contract
|
1. Deploy a new Version of the contract/app (see `/apps/*`)
|
||||||
2. Register the new version at the Registry:
|
2. Use the `aragon dao upgrade` command to "install" the new version for the DAO
|
||||||
`registry.addVersion('Token', NewToken.address)`
|
(`aragon dao upgrade <DAO address> <app name>`)
|
||||||
3. Set the new implementation address on the Proxy contract:
|
|
||||||
`registry.upgrade('Token', 2)`
|
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
|
4
apps/contribution/.gitignore
vendored
Normal file
4
apps/contribution/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
.cache
|
||||||
|
dist
|
14
apps/contribution/.ipfsignore
Normal file
14
apps/contribution/.ipfsignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Git files
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
.cache
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Others
|
||||||
|
test
|
1
apps/contribution/README.md
Normal file
1
apps/contribution/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Kredits Contribution
|
38
apps/contribution/arapp.json
Normal file
38
apps/contribution/arapp.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"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.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"
|
||||||
|
}
|
146
apps/contribution/contracts/Contribution.sol
Normal file
146
apps/contribution/contracts/Contribution.sol
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
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, uint256 contributionId) public;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, Token }
|
||||||
|
bytes32[4] public appIds;
|
||||||
|
|
||||||
|
struct ContributionData {
|
||||||
|
address contributor;
|
||||||
|
uint256 amount;
|
||||||
|
bool claimed;
|
||||||
|
bytes32 hashDigest;
|
||||||
|
uint8 hashFunction;
|
||||||
|
uint8 hashSize;
|
||||||
|
string tokenMetadataURL;
|
||||||
|
uint claimAfterBlock;
|
||||||
|
bool vetoed;
|
||||||
|
bool exists;
|
||||||
|
}
|
||||||
|
string internal name_;
|
||||||
|
string internal symbol_;
|
||||||
|
|
||||||
|
mapping(uint256 => address) contributionOwner;
|
||||||
|
mapping(address => uint256[]) ownedContributions;
|
||||||
|
|
||||||
|
mapping(uint256 => ContributionData) public contributions;
|
||||||
|
uint256 public contributionsCount;
|
||||||
|
|
||||||
|
uint256 public blocksToWait = 0;
|
||||||
|
|
||||||
|
event ContributionAdded(uint256 id, address indexed contributor, uint256 amount);
|
||||||
|
event ContributionClaimed(uint256 id, address indexed contributor, uint256 amount);
|
||||||
|
event ContributionVetoed(uint256 id, address vetoedByAccount);
|
||||||
|
|
||||||
|
function initialize(bytes32[4] _appIds) public onlyInit {
|
||||||
|
appIds = _appIds;
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTokenContract() public view returns (address) {
|
||||||
|
IKernel k = IKernel(kernel());
|
||||||
|
|
||||||
|
return k.getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Token)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function name() external view returns (string) {
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function symbol() external view returns (string) {
|
||||||
|
return symbol_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function balanceOf(address owner) public view returns (uint256) {
|
||||||
|
require(owner != address(0));
|
||||||
|
return ownedContributions[owner].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ownerOf(uint256 contributionId) public view returns (address) {
|
||||||
|
require(exists(contributionId));
|
||||||
|
return contributions[contributionId].contributor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenOfOwnerByIndex(address contributor, uint256 index) public view returns (uint256) {
|
||||||
|
return ownedContributions[contributor][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenMetadata(uint256 contributionId) public view returns (string) {
|
||||||
|
return contributions[contributionId].tokenMetadataURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContribution(uint256 contributionId) public view returns (uint256 id, address contributor, uint256 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint claimAfterBlock, bool exists, bool vetoed) {
|
||||||
|
id = contributionId;
|
||||||
|
ContributionData storage c = contributions[id];
|
||||||
|
return (
|
||||||
|
id,
|
||||||
|
c.contributor,
|
||||||
|
c.amount,
|
||||||
|
c.claimed,
|
||||||
|
c.hashDigest,
|
||||||
|
c.hashFunction,
|
||||||
|
c.hashSize,
|
||||||
|
c.claimAfterBlock,
|
||||||
|
c.exists,
|
||||||
|
c.vetoed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(uint256 amount, address contributorAccount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_CONTRIBUTION_ROLE) {
|
||||||
|
//require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint256[](0)), 'nope');
|
||||||
|
uint256 contributionId = contributionsCount + 1;
|
||||||
|
ContributionData storage c = contributions[contributionId];
|
||||||
|
c.exists = true;
|
||||||
|
c.amount = amount;
|
||||||
|
c.claimed = false;
|
||||||
|
c.contributor = contributorAccount;
|
||||||
|
c.hashDigest = hashDigest;
|
||||||
|
c.hashFunction = hashFunction;
|
||||||
|
c.hashSize = hashSize;
|
||||||
|
c.claimAfterBlock = block.number; // + blocksToWait;
|
||||||
|
|
||||||
|
contributionsCount++;
|
||||||
|
|
||||||
|
contributionOwner[contributionId] = contributorAccount;
|
||||||
|
ownedContributions[contributorAccount].push(contributionId);
|
||||||
|
|
||||||
|
emit ContributionAdded(contributionId, contributorAccount, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function veto(uint256 contributionId) public isInitialized auth(VETO_CONTRIBUTION_ROLE) {
|
||||||
|
ContributionData storage c = contributions[contributionId];
|
||||||
|
require(c.exists, 'NOT_FOUND');
|
||||||
|
require(!c.claimed, 'ALREADY_CLAIMED');
|
||||||
|
c.vetoed = true;
|
||||||
|
|
||||||
|
emit ContributionVetoed(contributionId, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function claim(uint256 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.claimAfterBlock, 'NOT_CLAIMABLE');
|
||||||
|
|
||||||
|
c.claimed = true;
|
||||||
|
address token = getTokenContract();
|
||||||
|
IToken(token).mintFor(c.contributor, c.amount, contributionId);
|
||||||
|
emit ContributionClaimed(contributionId, c.contributor, c.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exists(uint256 contributionId) view public returns (bool) {
|
||||||
|
return contributions[contributionId].exists;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
pragma solidity ^0.4.17;
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
contract Migrations {
|
contract Migrations {
|
||||||
address public owner;
|
address public owner;
|
||||||
@ -8,7 +8,7 @@ contract Migrations {
|
|||||||
if (msg.sender == owner) _;
|
if (msg.sender == owner) _;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Migrations() public {
|
constructor() public {
|
||||||
owner = msg.sender;
|
owner = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
4
apps/contribution/manifest.json
Normal file
4
apps/contribution/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Contribution",
|
||||||
|
"description": "Kredits contribution app"
|
||||||
|
}
|
5
apps/contribution/migrations/1_initial_migration.js
Normal file
5
apps/contribution/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
5
apps/contribution/migrations/2_deploy_contracts.js
Normal file
5
apps/contribution/migrations/2_deploy_contracts.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Contribution = artifacts.require('Contribution.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Contribution)
|
||||||
|
}
|
13977
apps/contribution/package-lock.json
generated
Normal file
13977
apps/contribution/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
apps/contribution/package.json
Normal file
27
apps/contribution/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "kredits-contribution",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@aragon/os": "^4.1.0",
|
||||||
|
"@aragon/cli": "^5.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"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": "npm run sync-assets && npm run build:script -- --no-minify && parcel serve app/index.html -p 8001 --out-dir dist/ --no-cache",
|
||||||
|
"test": "aragon contracts test",
|
||||||
|
"compile": "aragon contracts compile",
|
||||||
|
"sync-assets": "copy-aragon-ui-assets -n aragon-ui ./dist",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"keywords": []
|
||||||
|
}
|
5
apps/contribution/test/app.js
Normal file
5
apps/contribution/test/app.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const CounterApp = artifacts.require('Contribution.sol')
|
||||||
|
|
||||||
|
contract('Contribution', (accounts) => {
|
||||||
|
it('should be tested')
|
||||||
|
})
|
1
apps/contribution/truffle.js
Normal file
1
apps/contribution/truffle.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require("../../truffle.js");
|
4
apps/contributor/.gitignore
vendored
Normal file
4
apps/contributor/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
.cache
|
||||||
|
dist
|
14
apps/contributor/.ipfsignore
Normal file
14
apps/contributor/.ipfsignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Git files
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
.cache
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Others
|
||||||
|
test
|
1
apps/contributor/README.md
Normal file
1
apps/contributor/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Kredits Contributor
|
28
apps/contributor/arapp.json
Normal file
28
apps/contributor/arapp.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Manage contributors",
|
||||||
|
"id": "MANAGE_CONTRIBUTORS_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "kredits-contributor.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,10 +1,9 @@
|
|||||||
pragma solidity ^0.4.18;
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
// import basic ERC20 details to be able to call balanceOf
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
import 'zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol';
|
|
||||||
import './upgradeable/Upgradeable.sol';
|
|
||||||
|
|
||||||
contract Contributors is Upgradeable {
|
contract Contributor is AragonApp {
|
||||||
|
bytes32 public constant MANAGE_CONTRIBUTORS_ROLE = keccak256("MANAGE_CONTRIBUTORS_ROLE");
|
||||||
|
|
||||||
struct Contributor {
|
struct Contributor {
|
||||||
address account;
|
address account;
|
||||||
@ -19,24 +18,26 @@ contract Contributors is Upgradeable {
|
|||||||
mapping (uint => Contributor) public contributors;
|
mapping (uint => Contributor) public contributors;
|
||||||
uint256 public contributorsCount;
|
uint256 public contributorsCount;
|
||||||
|
|
||||||
|
// ensure alphabetic order
|
||||||
|
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||||
|
bytes32[4] public appIds;
|
||||||
|
|
||||||
event ContributorProfileUpdated(uint id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
|
event ContributorProfileUpdated(uint id, bytes32 oldIpfsHash, bytes32 newIpfsHash);
|
||||||
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
|
event ContributorAccountUpdated(uint id, address oldAccount, address newAccount);
|
||||||
event ContributorAdded(uint id, address account);
|
event ContributorAdded(uint id, address account);
|
||||||
|
|
||||||
modifier onlyCoreOrOperator() {
|
function initialize(address root,bytes32[4] _appIds) public onlyInit {
|
||||||
require(msg.sender == registry.getProxyFor('Operator') || addressIsCore(msg.sender));
|
uint _id = contributorsCount + 1;
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initialize(address sender) public payable {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
uint _id = 1;
|
|
||||||
Contributor storage c = contributors[_id];
|
Contributor storage c = contributors[_id];
|
||||||
c.exists = true;
|
c.exists = true;
|
||||||
c.isCore = true;
|
c.isCore = true;
|
||||||
c.account = sender;
|
c.account = root;
|
||||||
contributorIds[sender] = _id;
|
contributorIds[root] = _id;
|
||||||
contributorsCount += 1;
|
contributorsCount += 1;
|
||||||
|
|
||||||
|
appIds = _appIds;
|
||||||
|
|
||||||
|
initialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
function coreContributorsCount() view public returns (uint) {
|
function coreContributorsCount() view public returns (uint) {
|
||||||
@ -49,14 +50,14 @@ contract Contributors is Upgradeable {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateContributorAccount(uint id, address oldAccount, address newAccount) public onlyCoreOrOperator {
|
function updateContributorAccount(uint id, address oldAccount, address newAccount) public auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||||
contributorIds[oldAccount] = 0;
|
contributorIds[oldAccount] = 0;
|
||||||
contributorIds[newAccount] = id;
|
contributorIds[newAccount] = id;
|
||||||
contributors[id].account = newAccount;
|
contributors[id].account = newAccount;
|
||||||
ContributorAccountUpdated(id, oldAccount, newAccount);
|
ContributorAccountUpdated(id, oldAccount, newAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public onlyCoreOrOperator {
|
function updateContributorIpfsHash(uint id, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||||
Contributor storage c = contributors[id];
|
Contributor storage c = contributors[id];
|
||||||
bytes32 oldIpfsHash = c.ipfsHash;
|
bytes32 oldIpfsHash = c.ipfsHash;
|
||||||
c.ipfsHash = ipfsHash;
|
c.ipfsHash = ipfsHash;
|
||||||
@ -66,7 +67,7 @@ contract Contributors is Upgradeable {
|
|||||||
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
|
ContributorProfileUpdated(id, oldIpfsHash, c.ipfsHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public onlyCoreOrOperator {
|
function addContributor(address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore) public isInitialized auth(MANAGE_CONTRIBUTORS_ROLE) {
|
||||||
require(!addressExists(account));
|
require(!addressExists(account));
|
||||||
uint _id = contributorsCount + 1;
|
uint _id = contributorsCount + 1;
|
||||||
assert(!contributors[_id].exists); // this can not be acually
|
assert(!contributors[_id].exists); // this can not be acually
|
||||||
@ -80,7 +81,7 @@ contract Contributors is Upgradeable {
|
|||||||
contributorIds[account] = _id;
|
contributorIds[account] = _id;
|
||||||
|
|
||||||
contributorsCount += 1;
|
contributorsCount += 1;
|
||||||
ContributorAdded(_id, account);
|
emit ContributorAdded(_id, account);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCore(uint id) view public returns (bool) {
|
function isCore(uint id) view public returns (bool) {
|
||||||
@ -112,7 +113,7 @@ contract Contributors is Upgradeable {
|
|||||||
return contributors[id];
|
return contributors[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, uint balance, bool exists ) {
|
function getContributorById(uint _id) public view returns (uint id, address account, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, bool isCore, bool exists ) {
|
||||||
id = _id;
|
id = _id;
|
||||||
Contributor storage c = contributors[_id];
|
Contributor storage c = contributors[_id];
|
||||||
account = c.account;
|
account = c.account;
|
||||||
@ -121,8 +122,9 @@ contract Contributors is Upgradeable {
|
|||||||
hashSize = c.hashSize;
|
hashSize = c.hashSize;
|
||||||
isCore = c.isCore;
|
isCore = c.isCore;
|
||||||
exists = c.exists;
|
exists = c.exists;
|
||||||
|
}
|
||||||
|
|
||||||
ERC20Basic token = ERC20Basic(registry.getProxyFor('Token'));
|
function canPerform(address _who, address _where, bytes32 _what, uint256[] _how) public view returns (bool) {
|
||||||
balance = token.balanceOf(account);
|
return addressExists(_who);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
pragma solidity ^0.4.17;
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
contract Migrations {
|
contract Migrations {
|
||||||
address public owner;
|
address public owner;
|
||||||
@ -8,7 +8,7 @@ contract Migrations {
|
|||||||
if (msg.sender == owner) _;
|
if (msg.sender == owner) _;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Migrations() public {
|
constructor() public {
|
||||||
owner = msg.sender;
|
owner = msg.sender;
|
||||||
}
|
}
|
||||||
|
|
4
apps/contributor/manifest.json
Normal file
4
apps/contributor/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Contributor",
|
||||||
|
"description": "Kredits Contributor app"
|
||||||
|
}
|
5
apps/contributor/migrations/1_initial_migration.js
Normal file
5
apps/contributor/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
5
apps/contributor/migrations/2_deploy_contracts.js
Normal file
5
apps/contributor/migrations/2_deploy_contracts.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Contributor = artifacts.require('Contributor.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Contributor)
|
||||||
|
}
|
13977
apps/contributor/package-lock.json
generated
Normal file
13977
apps/contributor/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
apps/contributor/package.json
Normal file
27
apps/contributor/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "kredits-contributor",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@aragon/os": "^4.1.0",
|
||||||
|
"@aragon/cli": "^5.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"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": "",
|
||||||
|
"test": "aragon contracts test",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"keywords": []
|
||||||
|
}
|
5
apps/contributor/test/app.js
Normal file
5
apps/contributor/test/app.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const CounterApp = artifacts.require('CounterApp.sol')
|
||||||
|
|
||||||
|
contract('CounterApp', (accounts) => {
|
||||||
|
it('should be tested')
|
||||||
|
})
|
1
apps/contributor/truffle.js
Normal file
1
apps/contributor/truffle.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require("../../truffle.js");
|
1
apps/proposal/README.md
Normal file
1
apps/proposal/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Kredits Proposal
|
33
apps/proposal/arapp.json
Normal file
33
apps/proposal/arapp.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Add proposal",
|
||||||
|
"id": "ADD_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vote proposals",
|
||||||
|
"id": "VOTE_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "kredits-proposal.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"
|
||||||
|
}
|
134
apps/proposal/contracts/Proposal.sol
Normal file
134
apps/proposal/contracts/Proposal.sol
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
pragma solidity ^0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
import "@aragon/os/contracts/kernel/IKernel.sol";
|
||||||
|
|
||||||
|
interface IContributor {
|
||||||
|
function getContributorAddressById(uint256 contributorId) public view returns (address);
|
||||||
|
function getContributorIdByAddress(address contributorAccount) public view returns (uint256);
|
||||||
|
function exists(uint256 contributorId) public view returns (bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IContribution {
|
||||||
|
function add(uint256 amount, address contributor, 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, Token }
|
||||||
|
bytes32[4] public appIds;
|
||||||
|
|
||||||
|
struct Proposal {
|
||||||
|
address creatorAccount;
|
||||||
|
uint contributorId;
|
||||||
|
uint votesCount;
|
||||||
|
uint votesNeeded;
|
||||||
|
uint256 amount;
|
||||||
|
bool executed;
|
||||||
|
bytes32 hashDigest;
|
||||||
|
uint8 hashFunction;
|
||||||
|
uint8 hashSize;
|
||||||
|
uint256[] voterIds;
|
||||||
|
mapping (uint256 => bool) votes;
|
||||||
|
bool exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping(uint256 => Proposal) public proposals;
|
||||||
|
uint256 public proposalsCount;
|
||||||
|
|
||||||
|
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
|
||||||
|
|
||||||
|
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
|
||||||
|
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
|
||||||
|
|
||||||
|
function initialize(bytes32[4] _appIds) public onlyInit {
|
||||||
|
appIds = _appIds;
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributorContract() public view returns (address) {
|
||||||
|
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contributor)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContributionContract() public view returns (address) {
|
||||||
|
return IKernel(kernel()).getApp(KERNEL_APP_ADDR_NAMESPACE, appIds[uint8(Apps.Contribution)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addProposal(uint contributorId, uint256 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public isInitialized auth(ADD_PROPOSAL_ROLE) {
|
||||||
|
require(IContributor(getContributorContract()).exists(contributorId), 'CONTRIBUTOR_NOT_FOUND');
|
||||||
|
|
||||||
|
uint256 proposalId = proposalsCount + 1;
|
||||||
|
uint256 _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(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256[] 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(uint256 proposalId) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||||
|
Proposal storage p = proposals[proposalId];
|
||||||
|
require(!p.executed, 'ALREADY_EXECUTED');
|
||||||
|
uint256 voterId = IContributor(getContributorContract()).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(uint256[] _proposalIds) public isInitialized auth(VOTE_PROPOSAL_ROLE) {
|
||||||
|
for (uint256 i = 0; i < _proposalIds.length; i++) {
|
||||||
|
vote(_proposalIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeProposal(uint proposalId) private {
|
||||||
|
Proposal storage p = proposals[proposalId];
|
||||||
|
require(!p.executed, 'ALREADY_EXECUTED');
|
||||||
|
require(p.votesCount >= p.votesNeeded, 'MISSING_VOTES');
|
||||||
|
|
||||||
|
p.executed = true;
|
||||||
|
address contributorAccount = IContributor(getContributorContract()).getContributorAddressById(p.contributorId);
|
||||||
|
IContribution(getContributionContract()).add(p.amount, contributorAccount, p.hashDigest, p.hashFunction, p.hashSize);
|
||||||
|
emit ProposalExecuted(proposalId, p.contributorId, p.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
apps/proposal/contracts/misc/Migrations.sol
Normal file
23
apps/proposal/contracts/misc/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
4
apps/proposal/manifest.json
Normal file
4
apps/proposal/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Proposal",
|
||||||
|
"description": "Kredits proposal app"
|
||||||
|
}
|
5
apps/proposal/migrations/1_initial_migration.js
Normal file
5
apps/proposal/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
13977
apps/proposal/package-lock.json
generated
Normal file
13977
apps/proposal/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
apps/proposal/package.json
Normal file
27
apps/proposal/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "kredits-proposal",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@aragon/os": "^4.1.0",
|
||||||
|
"@aragon/cli": "^5.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"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": "",
|
||||||
|
"test": "aragon contracts test",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"keywords": []
|
||||||
|
}
|
1
apps/proposal/truffle.js
Normal file
1
apps/proposal/truffle.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require("../../truffle.js");
|
4
apps/token/.gitignore
vendored
Normal file
4
apps/token/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
build
|
||||||
|
.cache
|
||||||
|
dist
|
14
apps/token/.ipfsignore
Normal file
14
apps/token/.ipfsignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Git files
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
.cache
|
||||||
|
node_modules
|
||||||
|
build
|
||||||
|
|
||||||
|
# Lock files
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Others
|
||||||
|
test
|
1
apps/token/README.md
Normal file
1
apps/token/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Kredits Token
|
28
apps/token/arapp.json
Normal file
28
apps/token/arapp.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Mint token",
|
||||||
|
"id": "MINT_TOKEN_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "kredits-token.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"
|
||||||
|
}
|
173
apps/token/contracts/ERC20Token.sol
Normal file
173
apps/token/contracts/ERC20Token.sol
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
apps/token/contracts/Token.sol
Normal file
25
apps/token/contracts/Token.sol
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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, Token }
|
||||||
|
bytes32[4] public appIds;
|
||||||
|
|
||||||
|
event LogMint(address indexed recipient, uint256 amount, uint256 contributionId);
|
||||||
|
|
||||||
|
function initialize(bytes32[4] _appIds) public onlyInit {
|
||||||
|
appIds = _appIds;
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mintFor(address contributorAccount, uint256 amount, uint256 contributionId) public isInitialized auth(MINT_TOKEN_ROLE) {
|
||||||
|
_mint(contributorAccount, amount);
|
||||||
|
emit LogMint(contributorAccount, amount, contributionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
23
apps/token/contracts/misc/Migrations.sol
Normal file
23
apps/token/contracts/misc/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
4
apps/token/manifest.json
Normal file
4
apps/token/manifest.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "Token",
|
||||||
|
"description": "Kredits token app"
|
||||||
|
}
|
5
apps/token/migrations/1_initial_migration.js
Normal file
5
apps/token/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Migrations = artifacts.require('./Migrations.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Migrations)
|
||||||
|
}
|
5
apps/token/migrations/2_deploy_contracts.js
Normal file
5
apps/token/migrations/2_deploy_contracts.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var Token = artifacts.require('Token.sol')
|
||||||
|
|
||||||
|
module.exports = function (deployer) {
|
||||||
|
deployer.deploy(Token)
|
||||||
|
}
|
13977
apps/token/package-lock.json
generated
Normal file
13977
apps/token/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
apps/token/package.json
Normal file
27
apps/token/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "kredits-token",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@aragon/os": "^4.1.0",
|
||||||
|
"@aragon/cli": "^5.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"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": "",
|
||||||
|
"test": "aragon contracts test",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"keywords": []
|
||||||
|
}
|
5
apps/token/test/app.js
Normal file
5
apps/token/test/app.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const CounterApp = artifacts.require('CounterApp.sol')
|
||||||
|
|
||||||
|
contract('CounterApp', (accounts) => {
|
||||||
|
it('should be tested')
|
||||||
|
})
|
1
apps/token/truffle.js
Normal file
1
apps/token/truffle.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require("../../truffle.js");
|
60
arapp.json
Normal file
60
arapp.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"roles": [
|
||||||
|
{
|
||||||
|
"name": "Add contributions",
|
||||||
|
"id": "ADD_CONTRIBUTION_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Veto contributions",
|
||||||
|
"id": "VETO_CONTRIBUTION_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Manage contributors",
|
||||||
|
"id": "MANAGE_CONTRIBUTORS_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mint token",
|
||||||
|
"id": "MINT_TOKEN_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Add proposal",
|
||||||
|
"id": "ADD_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Vote proposal",
|
||||||
|
"id": "VOTE_PROPOSAL_ROLE",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"environments": {
|
||||||
|
"development": {
|
||||||
|
"network": "development",
|
||||||
|
"apm": "aragonpm.eth",
|
||||||
|
"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1",
|
||||||
|
"appName": "dummy.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"rinkeby": {
|
||||||
|
"network": "rinkeby",
|
||||||
|
"registry": "0x98Df287B6C145399Aaa709692c8D308357bC085D",
|
||||||
|
"wsRPC": "wss://rinkeby.eth.aragon.network/ws",
|
||||||
|
"daoFactory": "0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d",
|
||||||
|
"appName": "dummy.open.aragonpm.eth",
|
||||||
|
"apm": "open.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"kovan": {
|
||||||
|
"network": "kovan",
|
||||||
|
"appName": "dummy.aragonpm.eth"
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"network": "development",
|
||||||
|
"appName": "dummy.aragonpm.eth",
|
||||||
|
"apm": "open.aragonpm.eth"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "contracts/misc/DummyApp.sol"
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
let contractCalls = [
|
let contractCalls = [
|
||||||
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'preson', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, {gasLimit: 200000}]],
|
['Contributor', 'add', [{ account: '0x7e8f313c56f809188313aa274fa67ee58c31515d', name: 'bumi', isCore: true, kind: 'preson', url: '', github_username: 'bumi', github_uid: 318, wiki_username: 'bumi' }, {gasLimit: 200000}]],
|
||||||
['Contributor', 'add', [{ account: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', name: 'raucau', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, {gasLimit: 200000}]],
|
['Contributor', 'add', [{ account: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', name: 'raucau', isCore: true, kind: 'person', url: '', github_username: 'skddc', github_uid: 842, wiki_username: 'raucau' }, {gasLimit: 200000}]],
|
||||||
['Operator', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
['Proposal', 'addProposal', [{ contributorId: 2, amount: 42, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
||||||
['Operator', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
['Proposal', 'addProposal', [{ contributorId: 3, amount: 23, kind: 'code', description: 'runs the seeds', url: '' }, {gasLimit: 350000}]],
|
||||||
['Operator', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
|
['Proposal', 'addProposal', [{contributorId: 3, amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
|
||||||
['Operator', 'vote', ['1', {gasLimit: 250000}]]
|
['Proposal', 'vote', [1, {gasLimit: 550000}]],
|
||||||
|
['Contribution', 'addContribution', [{contributorAccount: '0xa502eb4021f3b9ab62f75b57a94e1cfbf81fd827', amount: 100, kind: 'code', description: 'hacks on kredits', url: '' }, {gasLimit: 350000}]],
|
||||||
];
|
];
|
||||||
let funds = [
|
let funds = [
|
||||||
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
|
'0x7e8f313c56f809188313aa274fa67ee58c31515d',
|
||||||
|
87
contracts/KreditsKit.sol
Normal file
87
contracts/KreditsKit.sol
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
import "@aragon/os/contracts/kernel/Kernel.sol";
|
||||||
|
import "@aragon/os/contracts/acl/ACL.sol";
|
||||||
|
|
||||||
|
import "@aragon/kits-base/contracts/KitBase.sol";
|
||||||
|
|
||||||
|
import "../apps/contribution/contracts/Contribution.sol";
|
||||||
|
import "../apps/contributor/contracts/Contributor.sol";
|
||||||
|
import "../apps/token/contracts/Token.sol";
|
||||||
|
import "../apps/proposal/contracts/Proposal.sol";
|
||||||
|
|
||||||
|
contract KreditsKit is KitBase {
|
||||||
|
|
||||||
|
// ensure alphabetic order
|
||||||
|
enum Apps { Contribution, Contributor, Proposal, Token }
|
||||||
|
bytes32[4] public appIds;
|
||||||
|
|
||||||
|
event DeployInstance(address dao);
|
||||||
|
event InstalledApp(address dao, address appProxy, bytes32 appId);
|
||||||
|
|
||||||
|
constructor (DAOFactory _fac, ENS _ens, bytes32[4] _appIds) public KitBase(_fac, _ens) {
|
||||||
|
appIds = _appIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function newInstance() public returns (Kernel dao) {
|
||||||
|
address root = msg.sender;
|
||||||
|
dao = fac.newDAO(this);
|
||||||
|
ACL acl = ACL(dao.acl());
|
||||||
|
|
||||||
|
acl.createPermission(this, dao, dao.APP_MANAGER_ROLE(), this);
|
||||||
|
|
||||||
|
Contributor contributor = Contributor(_installApp(dao, appIds[uint8(Apps.Contributor)]));
|
||||||
|
contributor.initialize(root, appIds);
|
||||||
|
acl.createPermission(root, contributor, contributor.MANAGE_CONTRIBUTORS_ROLE(), root);
|
||||||
|
|
||||||
|
Token token = Token(_installApp(dao, appIds[uint8(Apps.Token)]));
|
||||||
|
token.initialize(appIds);
|
||||||
|
|
||||||
|
Contribution contribution = Contribution(_installApp(dao, appIds[uint8(Apps.Contribution)]));
|
||||||
|
contribution.initialize(appIds);
|
||||||
|
|
||||||
|
Proposal proposal = Proposal(_installApp(dao, appIds[uint8(Apps.Proposal)]));
|
||||||
|
proposal.initialize(appIds);
|
||||||
|
|
||||||
|
acl.createPermission(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), this);
|
||||||
|
acl.createPermission(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), this);
|
||||||
|
acl.grantPermission(proposal, contribution, contribution.ADD_CONTRIBUTION_ROLE());
|
||||||
|
|
||||||
|
uint256[] memory params = new uint256[](1);
|
||||||
|
params[0] = uint256(203) << 248 | uint256(1) << 240 | uint240(contributor);
|
||||||
|
acl.grantPermissionP(root, contribution, contribution.ADD_CONTRIBUTION_ROLE(), params);
|
||||||
|
acl.grantPermissionP(root, contribution, contribution.VETO_CONTRIBUTION_ROLE(), params);
|
||||||
|
|
||||||
|
//acl.setPermissionManager(this, proposal, proposal.VOTE_PROPOSAL_ROLE();
|
||||||
|
acl.createPermission(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), this);
|
||||||
|
acl.grantPermissionP(root, proposal, proposal.VOTE_PROPOSAL_ROLE(), params);
|
||||||
|
|
||||||
|
acl.createPermission(root, proposal, proposal.ADD_PROPOSAL_ROLE(), this);
|
||||||
|
acl.grantPermissionP(root, proposal, proposal.ADD_PROPOSAL_ROLE(), params);
|
||||||
|
|
||||||
|
acl.setPermissionManager(root, proposal, proposal.VOTE_PROPOSAL_ROLE());
|
||||||
|
acl.setPermissionManager(root, proposal, proposal.ADD_PROPOSAL_ROLE());
|
||||||
|
acl.setPermissionManager(root, contribution, contribution.ADD_CONTRIBUTION_ROLE());
|
||||||
|
acl.setPermissionManager(root, contribution, contribution.VETO_CONTRIBUTION_ROLE());
|
||||||
|
|
||||||
|
acl.createPermission(root, token, token.MINT_TOKEN_ROLE(), this);
|
||||||
|
acl.grantPermission(contribution, token, token.MINT_TOKEN_ROLE());
|
||||||
|
acl.setPermissionManager(root, token, token.MINT_TOKEN_ROLE());
|
||||||
|
|
||||||
|
|
||||||
|
cleanupDAOPermissions(dao, acl, root);
|
||||||
|
|
||||||
|
emit DeployInstance(dao);
|
||||||
|
return dao;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _installApp(Kernel _dao, bytes32 _appId) internal returns (AragonApp) {
|
||||||
|
address baseAppAddress = latestVersionAppBase(_appId);
|
||||||
|
require(baseAppAddress != address(0), "App should be deployed");
|
||||||
|
AragonApp appProxy = AragonApp(_dao.newAppInstance(_appId, baseAppAddress, new bytes(0), true));
|
||||||
|
|
||||||
|
emit InstalledApp(_dao, appProxy, _appId);
|
||||||
|
return appProxy;
|
||||||
|
}
|
||||||
|
}
|
@ -1,130 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
// ToDo: only load interfaces
|
|
||||||
import './Token.sol';
|
|
||||||
import './Contributors.sol';
|
|
||||||
|
|
||||||
contract Operator is Upgradeable {
|
|
||||||
|
|
||||||
struct Proposal {
|
|
||||||
address creatorAccount;
|
|
||||||
uint contributorId;
|
|
||||||
uint votesCount;
|
|
||||||
uint votesNeeded;
|
|
||||||
uint256 amount;
|
|
||||||
bool executed;
|
|
||||||
bytes32 ipfsHash;
|
|
||||||
uint8 hashFunction;
|
|
||||||
uint8 hashSize;
|
|
||||||
uint256[] voterIds;
|
|
||||||
mapping (uint256 => bool) votes;
|
|
||||||
bool exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping(uint256 => Proposal) public proposals;
|
|
||||||
uint256 public proposalsCount;
|
|
||||||
|
|
||||||
event ProposalCreated(uint256 id, address creatorAccount, uint256 contributorId, uint256 amount);
|
|
||||||
event ProposalVoted(uint256 id, uint256 voterId, uint256 totalVotes);
|
|
||||||
event ProposalExecuted(uint256 id, uint256 contributorId, uint256 amount);
|
|
||||||
|
|
||||||
modifier coreOnly() {
|
|
||||||
require(contributorsContract().addressIsCore(msg.sender));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
modifier contributorOnly() {
|
|
||||||
require(contributorsContract().addressExists(msg.sender));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
modifier noEther() {
|
|
||||||
require(msg.value == 0);
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
function contributorsContract() view public returns (Contributors) {
|
|
||||||
return Contributors(registry.getProxyFor('Contributors'));
|
|
||||||
}
|
|
||||||
function tokenContract() view public returns (Token) {
|
|
||||||
return Token(registry.getProxyFor('Token'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function contributorsCount() view public returns (uint) {
|
|
||||||
return contributorsContract().contributorsCount();
|
|
||||||
}
|
|
||||||
function coreContributorsCount() view public returns (uint) {
|
|
||||||
return contributorsContract().coreContributorsCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProposal(uint contributorId, uint256 amount, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize) public {
|
|
||||||
require(contributorsContract().exists(contributorId));
|
|
||||||
|
|
||||||
uint256 proposalId = proposalsCount + 1;
|
|
||||||
uint256 _votesNeeded = contributorsContract().coreContributorsCount() / 100 * 75;
|
|
||||||
|
|
||||||
var p = proposals[proposalId];
|
|
||||||
p.creatorAccount = msg.sender;
|
|
||||||
p.contributorId = contributorId;
|
|
||||||
p.amount = amount;
|
|
||||||
p.ipfsHash = ipfsHash;
|
|
||||||
p.hashFunction = hashFunction;
|
|
||||||
p.hashSize = hashSize;
|
|
||||||
p.votesCount = 0;
|
|
||||||
p.votesNeeded = _votesNeeded;
|
|
||||||
p.exists = true;
|
|
||||||
|
|
||||||
proposalsCount++;
|
|
||||||
ProposalCreated(proposalId, msg.sender, p.contributorId, p.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProposal(uint proposalId) public view returns (uint256 id, address creatorAccount, uint256 contributorId, uint256 votesCount, uint256 votesNeeded, uint256 amount, bool executed, bytes32 ipfsHash, uint8 hashFunction, uint8 hashSize, uint256[] 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.ipfsHash,
|
|
||||||
p.hashFunction,
|
|
||||||
p.hashSize,
|
|
||||||
p.voterIds,
|
|
||||||
p.exists
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function vote(uint256 proposalId) public coreOnly {
|
|
||||||
var p = proposals[proposalId];
|
|
||||||
require(!p.executed);
|
|
||||||
uint256 voterId = contributorsContract().getContributorIdByAddress(msg.sender);
|
|
||||||
require(p.votes[voterId] != true);
|
|
||||||
p.voterIds.push(voterId);
|
|
||||||
p.votes[voterId] = true;
|
|
||||||
|
|
||||||
p.votesCount++;
|
|
||||||
if (p.votesCount >= p.votesNeeded) {
|
|
||||||
executeProposal(proposalId);
|
|
||||||
}
|
|
||||||
ProposalVoted(proposalId, voterId, p.votesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
function batchVote(uint256[] _proposalIds) public coreOnly {
|
|
||||||
for (uint256 i = 0; i < _proposalIds.length; i++) {
|
|
||||||
vote(_proposalIds[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function executeProposal(uint proposalId) private {
|
|
||||||
|
|
||||||
var p = proposals[proposalId];
|
|
||||||
require(!p.executed);
|
|
||||||
require(p.votesCount >= p.votesNeeded);
|
|
||||||
address recipientAddress = contributorsContract().getContributorAddressById(p.contributorId);
|
|
||||||
tokenContract().mintFor(recipientAddress, p.amount, proposalId);
|
|
||||||
p.executed = true;
|
|
||||||
ProposalExecuted(proposalId, p.contributorId, p.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import 'zeppelin-solidity/contracts/token/ERC20/BasicToken.sol';
|
|
||||||
import './upgradeable/Upgradeable.sol';
|
|
||||||
|
|
||||||
contract Token is Upgradeable, BasicToken {
|
|
||||||
string public name;
|
|
||||||
string public symbol;
|
|
||||||
uint8 public decimals;
|
|
||||||
|
|
||||||
event LogMint(address indexed recipient, uint256 amount, uint256 proposalId);
|
|
||||||
|
|
||||||
function initialize(address sender) public payable {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
name = 'Kredits';
|
|
||||||
symbol = 'K';
|
|
||||||
decimals = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mintFor(address contributorAccount, uint256 amount, uint proposalId) onlyRegistryContractFor('Operator') public {
|
|
||||||
totalSupply_ = totalSupply_.add(amount);
|
|
||||||
balances[contributorAccount] = balances[contributorAccount].add(amount);
|
|
||||||
|
|
||||||
LogMint(contributorAccount, amount, proposalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
13
contracts/misc/APMNamehashOpen.sol
Normal file
13
contracts/misc/APMNamehashOpen.sol
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apm/APMNamehash.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract APMNamehashOpen is APMNamehash {
|
||||||
|
bytes32 public constant OPEN_TITLE = keccak256("open");
|
||||||
|
bytes32 public constant OPEN_APM_NODE = keccak256(abi.encodePacked(APM_NODE, OPEN_TITLE));
|
||||||
|
|
||||||
|
function apmNamehashOpen(string name) internal pure returns (bytes32) {
|
||||||
|
return keccak256(abi.encodePacked(OPEN_APM_NODE, keccak256(name)));
|
||||||
|
}
|
||||||
|
}
|
16
contracts/misc/DummyApp.sol
Normal file
16
contracts/misc/DummyApp.sol
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
pragma solidity 0.4.24;
|
||||||
|
|
||||||
|
import "@aragon/os/contracts/apps/AragonApp.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// This is a "Dummy" app which's only purpose to exist is because
|
||||||
|
// Aragon's CLI still doesn't support running a Kit inside a project
|
||||||
|
// which isn't considered to be a "valid" Aragon project.
|
||||||
|
// It requires us to have an arrap.json file pointing to the contract
|
||||||
|
// and a manifest.json file which describes the front-end structure.
|
||||||
|
contract DummyApp is AragonApp {
|
||||||
|
function initialize() public onlyInit {
|
||||||
|
initialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
23
contracts/misc/Migrations.sol
Normal file
23
contracts/misc/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
pragma solidity ^0.4.4;
|
||||||
|
|
||||||
|
contract Migrations {
|
||||||
|
address public owner;
|
||||||
|
uint public last_completed_migration;
|
||||||
|
|
||||||
|
modifier restricted() {
|
||||||
|
if (msg.sender == owner) _;
|
||||||
|
}
|
||||||
|
|
||||||
|
function 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,53 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title IRegistry
|
|
||||||
* @dev This contract represents the interface of a registry contract
|
|
||||||
*/
|
|
||||||
interface IRegistry {
|
|
||||||
/**
|
|
||||||
* @dev This event will be emitted every time a new proxy is created
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param proxy representing the address of the proxy created
|
|
||||||
*/
|
|
||||||
event ProxyCreated(string name, address proxy);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev This event will be emitted every time a new implementation is registered
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param version representing the version name of the registered implementation
|
|
||||||
* @param implementation representing the address of the registered implementation
|
|
||||||
*/
|
|
||||||
event VersionAdded(string name, uint version, address implementation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev This event will be emitted every time a proxy is upgraded to a new version
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param version representing the version name of the registered implementation
|
|
||||||
*/
|
|
||||||
event ProxyImplementationUpgraded(string name, uint version);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Registers a new version with its implementation address
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param implementation representing the address of the new implementation to be registered
|
|
||||||
*/
|
|
||||||
function addVersion(string name, address implementation) public;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the implementation for a given version
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @param version to query the implementation of
|
|
||||||
* @return address of the implementation registered for the given version
|
|
||||||
*/
|
|
||||||
function getVersion(string name, uint version) public view returns (address);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the latest address of the implementation
|
|
||||||
* @param name of the contract, as specified in the registry
|
|
||||||
* @return address of the implementation registered for the latest version
|
|
||||||
*/
|
|
||||||
function getLatestVersion(string name) public view returns (address);
|
|
||||||
|
|
||||||
function getProxyFor(string name) public view returns (address);
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Proxy
|
|
||||||
* @dev Gives the possibility to delegate any call to a foreign implementation.
|
|
||||||
*/
|
|
||||||
contract Proxy {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the implementation where every call will be delegated.
|
|
||||||
* @return address of the implementation to which it will be delegated
|
|
||||||
*/
|
|
||||||
function implementation() public view returns (address);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Fallback function allowing to perform a delegatecall to the given implementation.
|
|
||||||
* This function will return whatever the implementation call returns
|
|
||||||
*/
|
|
||||||
function () payable public {
|
|
||||||
address _impl = implementation();
|
|
||||||
require(_impl != address(0));
|
|
||||||
bytes memory data = msg.data;
|
|
||||||
|
|
||||||
assembly {
|
|
||||||
let result := delegatecall(gas, _impl, add(data, 0x20), mload(data), 0, 0)
|
|
||||||
let size := returndatasize
|
|
||||||
|
|
||||||
let ptr := mload(0x40)
|
|
||||||
returndatacopy(ptr, 0, size)
|
|
||||||
|
|
||||||
switch result
|
|
||||||
case 0 { revert(ptr, size) }
|
|
||||||
default { return(ptr, size) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './IRegistry.sol';
|
|
||||||
import './Upgradeable.sol';
|
|
||||||
import './UpgradeabilityProxy.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Registry
|
|
||||||
* @dev This contract works as a registry of versions, it holds the implementations for the registered versions.
|
|
||||||
*/
|
|
||||||
contract Registry is IRegistry {
|
|
||||||
// mapping of contract names to versions to implementation
|
|
||||||
// "Token" => "1.0.0" => "0x123"
|
|
||||||
mapping(bytes32 => mapping(uint => address)) public versions;
|
|
||||||
|
|
||||||
// current version for a certain contract
|
|
||||||
mapping(bytes32 => uint) public currentVersions;
|
|
||||||
|
|
||||||
// mapping of the contract names to the proxy addresses
|
|
||||||
mapping(bytes32 => address) public proxies;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Registers a new version with its implementation address
|
|
||||||
* @param name of the contract
|
|
||||||
* @param implementation representing the address of the new implementation to be registered
|
|
||||||
*/
|
|
||||||
function addVersion(string name, address implementation) public {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
currentVersions[key] = currentVersions[key] + 1;
|
|
||||||
uint version = currentVersions[key];
|
|
||||||
require(versions[key][version] == 0x0);
|
|
||||||
versions[key][version] = implementation;
|
|
||||||
VersionAdded(name, version, implementation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the implementation for a given version
|
|
||||||
* @param name of the contract
|
|
||||||
* @param version to query the implementation of
|
|
||||||
* @return address of the implementation registered for the given version
|
|
||||||
*/
|
|
||||||
function getVersion(string name, uint version) public view returns (address) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
return versions[key][version];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLatestVersion(string name) public view returns (address) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
uint current = currentVersions[key];
|
|
||||||
return getVersion(name, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProxyFor(string name) public view returns (address) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
return proxies[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgrade(string name, uint version) public {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
UpgradeabilityProxy(proxies[key]).upgradeTo(version);
|
|
||||||
ProxyImplementationUpgraded(name, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgradeToLatest(string name) public {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
uint current = currentVersions[key];
|
|
||||||
upgrade(name, current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Creates an upgradeable proxy
|
|
||||||
* @param name of the contract
|
|
||||||
* @param version representing the first version to be set for the proxy
|
|
||||||
* @return address of the new proxy created
|
|
||||||
*/
|
|
||||||
function createProxy(string name, uint version) public payable returns (UpgradeabilityProxy) {
|
|
||||||
bytes32 key = keccak256(name);
|
|
||||||
require(proxies[key] == 0x0);
|
|
||||||
UpgradeabilityProxy proxy = new UpgradeabilityProxy(name, version);
|
|
||||||
proxies[key] = address(proxy);
|
|
||||||
Upgradeable(proxy).initialize.value(msg.value)(msg.sender);
|
|
||||||
ProxyCreated(name, proxy);
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './Proxy.sol';
|
|
||||||
import './IRegistry.sol';
|
|
||||||
import './UpgradeabilityStorage.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title UpgradeabilityProxy
|
|
||||||
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded
|
|
||||||
*/
|
|
||||||
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage {
|
|
||||||
|
|
||||||
function UpgradeabilityProxy(string _name, uint _version) public {
|
|
||||||
_proxiedContractName = _name;
|
|
||||||
registry = IRegistry(msg.sender);
|
|
||||||
upgradeTo(_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Upgrades the implementation to the requested version
|
|
||||||
* @param _version representing the version name of the new implementation to be set
|
|
||||||
*/
|
|
||||||
function upgradeTo(uint _version) public {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
_implementation = registry.getVersion(_proxiedContractName, _version);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './IRegistry.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title UpgradeabilityStorage
|
|
||||||
* @dev This contract holds all the necessary state variables to support the upgrade functionality
|
|
||||||
*/
|
|
||||||
contract UpgradeabilityStorage {
|
|
||||||
// Versions registry
|
|
||||||
IRegistry internal registry;
|
|
||||||
|
|
||||||
// Address of the current implementation
|
|
||||||
address internal _implementation;
|
|
||||||
|
|
||||||
// contract name
|
|
||||||
string public _proxiedContractName;
|
|
||||||
|
|
||||||
|
|
||||||
modifier requireRegistry() {
|
|
||||||
require(address(registry) != 0x0);
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
modifier onlyRegistryContractFor(string name) {
|
|
||||||
require(address(registry) != 0x0);
|
|
||||||
require(msg.sender == registry.getProxyFor(name));
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Tells the address of the current implementation
|
|
||||||
* @return address of the current implementation
|
|
||||||
*/
|
|
||||||
function implementation() public view returns (address) {
|
|
||||||
return _implementation;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
pragma solidity ^0.4.18;
|
|
||||||
|
|
||||||
import './UpgradeabilityStorage.sol';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @title Upgradeable
|
|
||||||
* @dev This contract holds all the minimum required functionality for a behavior to be upgradeable.
|
|
||||||
* This means, required state variables for owned upgradeability purpose and simple initialization validation.
|
|
||||||
*/
|
|
||||||
contract Upgradeable is UpgradeabilityStorage {
|
|
||||||
/**
|
|
||||||
* @dev Validates the caller is the versions registry.
|
|
||||||
* THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER
|
|
||||||
* @param sender representing the address deploying the initial behavior of the contract
|
|
||||||
*/
|
|
||||||
function initialize(address sender) public payable {
|
|
||||||
require(msg.sender == address(registry));
|
|
||||||
}
|
|
||||||
}
|
|
28
deployments/rinkeby/contribution.md
Normal file
28
deployments/rinkeby/contribution.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Contribution deployments
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v3.0.0 appids
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-contribution.open.aragonpm.eth v3.0.0:
|
||||||
|
ℹ Contract address: 0x9b4954834f070380faf9B6CB3794C7eA92d3B506
|
||||||
|
ℹ Content (ipfs): Qma3yvT5AmBbJHL8WkpXHs7aCFqpGd9ZaMpbiz3cT6Un59
|
||||||
|
ℹ Transaction hash: 0xc2b2d19e1b6d68ba50ed051a35c22eac74c69bd50665aed65488f9209171d382
|
||||||
|
|
||||||
|
## 2019-03-25
|
||||||
|
|
||||||
|
### v2.0.0 updated appid hashes
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-contribution.open.aragonpm.eth v2.0.0:
|
||||||
|
ℹ Contract address: 0x2765F13d82B83C0C9d770715e1fB376ab01C0361
|
||||||
|
ℹ Content (ipfs): QmYiv1cMh6cj8pcbmaTUAWxf93k4UhN8mPJWeqEo7BEkMx
|
||||||
|
ℹ Transaction hash: 0xe4841a6552527c8fbeb6aa44769f95ce60d99413958c6289f67e8ec83823ee96
|
||||||
|
|
||||||
|
|
||||||
|
### v1.0.0
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-contribution.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0x1F460107361047064d982f61002D1BdEb02Ce705
|
||||||
|
ℹ Content (ipfs): QmQ6rTWCGcseCmGb6thYG2kwijHk6W84qaFPeftZ4bUY2k
|
||||||
|
ℹ Transaction hash: 0xeee90c5d18a8cb8ac845f52a4202cd62b27c23b4251b69c3681a854786b6e336
|
||||||
|
|
19
deployments/rinkeby/contributor.md
Normal file
19
deployments/rinkeby/contributor.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Contributor deployments
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v2.0.0 support for appids
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-contributor.open.aragonpm.eth v2.0.0:
|
||||||
|
ℹ Contract address: 0xf8e7B45b3c5A98dbE3D69C6828052dfD9fe2dc69
|
||||||
|
ℹ Content (ipfs): QmTDduMJVUVcqz5SgVEns1xwY9LqYrMoAyYcx3pMrPxn6i
|
||||||
|
ℹ Transaction hash: 0x48b38f32f5dcb52a0a8603743b66a7ca490fec04ccb1888dcfd37106bbd3b886
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-03-25
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-contributor.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0x6D80EFEE6F9A40AA86Ef7c4C95c8b2e453d260fb
|
||||||
|
ℹ Content (ipfs): QmbGSQPwi3AXHjDFG2fkMpJrBJLtyuaA6DVPMkFTfMpksn
|
||||||
|
ℹ Transaction hash: 0x26376c59dfdb617c35b740a0f110bf3040cdad0103593cdc788267a84d9847b9
|
||||||
|
|
27
deployments/rinkeby/dao.md
Normal file
27
deployments/rinkeby/dao.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Kredits deployment
|
||||||
|
|
||||||
|
### 2019-03-26
|
||||||
|
|
||||||
|
kredits/truffle-kredits@aragonos » truffle exec scripts/new-dao.js --network=rinkeby
|
||||||
|
Using network 'rinkeby'.
|
||||||
|
|
||||||
|
Deploying to networkId: 4
|
||||||
|
Using KreditsKit at: 0x1d6a9c2146a330575ee860eef9a012b5ff7caa68
|
||||||
|
|
||||||
|
Created new DAO at: 0x95a7ce185efc2d1f13efd2a00ee9247c51ea7009"
|
||||||
|
|
||||||
|
|
||||||
|
### 2019-03-25
|
||||||
|
|
||||||
|
kredits/truffle-kredits@aragonos » aragon contracts exec scripts/new-dao.js --network=rinkeby
|
||||||
|
ℹ Use of `--network` is deprecated and has been replaced with `--environment`. You may need to update your arapp.json
|
||||||
|
ℹ Passing the command to Truffle
|
||||||
|
Using network 'rinkeby'.
|
||||||
|
|
||||||
|
Deploying to networkId: 4
|
||||||
|
Using KreditsKit at: 0xf4f3963718e5c2b426dd5c3ef0ab4b31ffb7a318
|
||||||
|
|
||||||
|
Created new DAO at: 0x8b7c0bec9476ce08d9769a87d272b03b350712e2
|
||||||
|
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
https://rinkeby.etherscan.io/tx/0x4deb02b3740b3baa7735097d57c1456a5cb329ee741d123478fdb5114e2c305b
|
54
deployments/rinkeby/kreditskit.md
Normal file
54
deployments/rinkeby/kreditskit.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# KreditsKit deployments
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
Using network 'rinkeby'.
|
||||||
|
|
||||||
|
Deploying to networkId: 4
|
||||||
|
Using ENS at: 0x98Df287B6C145399Aaa709692c8D308357bC085D
|
||||||
|
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
|
||||||
|
Found apps: [contribution,contributor,proposal,token].open.aragonpm.eth
|
||||||
|
Deployed KreditsKit at: 0x1d6a9c2146a330575ee860eef9a012b5ff7caa68
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### fails
|
||||||
|
a few deployments to fix the deploy script
|
||||||
|
|
||||||
|
## 2019-03-25
|
||||||
|
|
||||||
|
### fixed kit with correct appids
|
||||||
|
|
||||||
|
kredits/truffle-kredits@aragonos » aragon contracts exec scripts/deploy-kit.js --debug --network=rinkeby
|
||||||
|
ℹ Use of `--network` is deprecated and has been replaced with `--environment`. You may need to update your arapp.json
|
||||||
|
ℹ Passing the command to Truffle
|
||||||
|
Using network 'rinkeby'.
|
||||||
|
|
||||||
|
Deploying to networkId: 4
|
||||||
|
Using ENS at: 0x98Df287B6C145399Aaa709692c8D308357bC085D
|
||||||
|
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
|
||||||
|
Found apps: [contribution,contributor,proposal,token].open.aragonpm.eth
|
||||||
|
Deployed KreditsKit at: 0xf4f3963718e5c2b426dd5c3ef0ab4b31ffb7a318
|
||||||
|
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
|
||||||
|
### test with fixed deploy script
|
||||||
|
kredits/truffle-kredits@aragonos » ENS=0x98df287b6c145399aaa709692c8d308357bc085d DAO_FACTORY=0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d truffle exec scripts/deploy-kit.js --network=rinkeby
|
||||||
|
Using network 'rinkeby'.
|
||||||
|
|
||||||
|
Deploying to networkId: 4
|
||||||
|
Using ENS at: 0x98df287b6c145399aaa709692c8d308357bc085d
|
||||||
|
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
|
||||||
|
Deployed KreditsKit at: 0x83afd3c99563fc467aec69e0187ffd53fc8faa76
|
||||||
|
|
||||||
|
### success
|
||||||
|
kredits/truffle-kredits@aragonos » ENS=0x98df287b6c145399aaa709692c8d308357bc085d DAO_FACTORY=0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d truffle exec scripts/deploy-kit.js --network=rinkeby
|
||||||
|
Using network 'rinkeby'.
|
||||||
|
|
||||||
|
Using ENS at: 0x98df287b6c145399aaa709692c8d308357bc085d
|
||||||
|
Using DAOFactory at: 0x2298d27a9b847c681d2b2c2828ab9d79013f5f1d
|
||||||
|
Deployed KreditsKit at: 0x1fd2f9206addaf86f3ef921a3b7c84400374ba68
|
||||||
|
|
||||||
|
### deployment script error:
|
||||||
|
deployment script failure at: https://rinkeby.etherscan.io/tx/0x3571b889b6b9b2b3f26dd0ee7fb82c7ece90b28d910078f2e06753d878832af4"
|
||||||
|
|
27
deployments/rinkeby/proposal.md
Normal file
27
deployments/rinkeby/proposal.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Proposal deployments
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v3.0.0 appids
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-proposal.open.aragonpm.eth v3.0.0:
|
||||||
|
ℹ Contract address: 0x9f47c31aBB6F53ff2fec09C0bF79c3337C20f5AA
|
||||||
|
ℹ Content (ipfs): Qmdxg33FxAQSH7U8oTApaM5SgUUC9btiA6tu5Hystzp5tV
|
||||||
|
ℹ Transaction hash: 0xceb572f3fff368b9f07fc835577529a113e2de8ff7972a51ffaf4c63acbda9eb
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-03-25
|
||||||
|
|
||||||
|
### v2.0.0 updated appid hashes
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-proposal.open.aragonpm.eth v2.0.0:
|
||||||
|
ℹ Contract address: 0x0a48dc5415f4d7A3B2B4a9aaD4A7a8775f923ce3
|
||||||
|
ℹ Content (ipfs): QmZej9xPJN7aTiQ7SzWziRuzEjjfMzjPakcGYcoN8QQBMH
|
||||||
|
ℹ Transaction hash: 0xf14c308b9f92b43a719fab2602c624dc2e11d0f629a0be9a6ec8ac47719ee9ca
|
||||||
|
|
||||||
|
### v1.0.0
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-proposal.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0xFc04eB3eF666507F96A22d330d8a852d8eC996EA
|
||||||
|
ℹ Content (ipfs): QmRTzoYr7B5f8gDGY8RmoodJkMm59D4jSJLZNRnhAd4wCg
|
||||||
|
ℹ Transaction hash: 0xce419d9d555551eefc624a7ac87c19b5d660eb9c4fe0da611cd5e28e3ee6844f
|
18
deployments/rinkeby/token.md
Normal file
18
deployments/rinkeby/token.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Token deployments
|
||||||
|
|
||||||
|
## 2019-03-26
|
||||||
|
|
||||||
|
### v2.0.0 support for appIds
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-token.open.aragonpm.eth v2.0.0:
|
||||||
|
ℹ Contract address: 0xc126919B3b30F14b848c2cc66Ec21A1e85a7c621
|
||||||
|
ℹ Content (ipfs): QmWB9JrWa93vtdv5VG2yohiWTbDiuzbz9P58apJdP3FnNP
|
||||||
|
ℹ Transaction hash: 0xa5ccd532a91d67b0616ee5505144988be4360440daf70dc2f5108dd2a59f3d68
|
||||||
|
|
||||||
|
|
||||||
|
## 2019-03-25
|
||||||
|
from account: 0x18f6d06de7e6d556b1bbb5875f8cfafb5eaef9c5
|
||||||
|
✔ Successfully published kredits-token.open.aragonpm.eth v1.0.0:
|
||||||
|
ℹ Contract address: 0xB4147Be6d4bcC790c6CbD3d508354DDfd3EbC8e6
|
||||||
|
ℹ Content (ipfs): QmQPue7WzQhaXbTLJwyZXSHaRY1u5ezWxyVmBP8bEgGoTE
|
||||||
|
ℹ Transaction hash: 0xa7866d81e236adfeaff6bf2d5dbc9f9757fbc1b0ce689357901194913fe93a3b"
|
BIN
docs/kredits-diagram.png
Normal file
BIN
docs/kredits-diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
1
lib/abis/ACL.json
Normal file
1
lib/abis/ACL.json
Normal file
File diff suppressed because one or more lines are too long
1
lib/abis/Contribution.json
Normal file
1
lib/abis/Contribution.json
Normal file
File diff suppressed because one or more lines are too long
1
lib/abis/Contributor.json
Normal file
1
lib/abis/Contributor.json
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"contributors","outputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"contributorIds","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldIpfsHash","type":"bytes32"},{"indexed":false,"name":"newIpfsHash","type":"bytes32"}],"name":"ContributorProfileUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"oldAccount","type":"address"},{"indexed":false,"name":"newAccount","type":"address"}],"name":"ContributorAccountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"account","type":"address"}],"name":"ContributorAdded","type":"event"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"oldAccount","type":"address"},{"name":"newAccount","type":"address"}],"name":"updateContributorAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"id","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"updateContributorIpfsHash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"}],"name":"addContributor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"isCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"exists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressIsCore","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"addressExists","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"account","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getContributorAddressById","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_id","type":"uint256"}],"name":"getContributorById","outputs":[{"name":"id","type":"uint256"},{"name":"account","type":"address"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"isCore","type":"bool"},{"name":"balance","type":"uint256"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]
|
|
1
lib/abis/Kernel.json
Normal file
1
lib/abis/Kernel.json
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_proxiedContractName","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sender","type":"address"}],"name":"initialize","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"creatorAccount","type":"address"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"voterId","type":"uint256"},{"indexed":false,"name":"totalVotes","type":"uint256"}],"name":"ProposalVoted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"id","type":"uint256"},{"indexed":false,"name":"contributorId","type":"uint256"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ProposalExecuted","type":"event"},{"constant":true,"inputs":[],"name":"contributorsContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenContract","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"coreContributorsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"contributorId","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"}],"name":"addProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"getProposal","outputs":[{"name":"id","type":"uint256"},{"name":"creatorAccount","type":"address"},{"name":"contributorId","type":"uint256"},{"name":"votesCount","type":"uint256"},{"name":"votesNeeded","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"executed","type":"bool"},{"name":"ipfsHash","type":"bytes32"},{"name":"hashFunction","type":"uint8"},{"name":"hashSize","type":"uint8"},{"name":"voterIds","type":"uint256[]"},{"name":"exists","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"proposalId","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_proposalIds","type":"uint256[]"}],"name":"batchVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]
|
|
1
lib/abis/Proposal.json
Normal file
1
lib/abis/Proposal.json
Normal file
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
[{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"currentVersions","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"proxies","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"},{"name":"","type":"uint256"}],"name":"versions","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"proxy","type":"address"}],"name":"ProxyCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"version","type":"uint256"},{"indexed":false,"name":"implementation","type":"address"}],"name":"VersionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"version","type":"uint256"}],"name":"ProxyImplementationUpgraded","type":"event"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"implementation","type":"address"}],"name":"addVersion","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"getVersion","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getLatestVersion","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getProxyFor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"upgradeToLatest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"version","type":"uint256"}],"name":"createProxy","outputs":[{"name":"","type":"address"}],"payable":true,"stateMutability":"payable","type":"function"}]
|
|
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
|||||||
{"42":"0x205fe1b3dac678b594c5f0535e7d158e38591f93","100":"0xa7fc9b1f678c41396b53904f94f50a42ff44d826"}
|
|
4
lib/addresses/KreditsKit.json
Normal file
4
lib/addresses/KreditsKit.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"4": "0x1d6a9c2146a330575ee860eef9a012b5ff7caa68",
|
||||||
|
"41787949": "0xa35aacdfccac54d3d96e0d29050c773b251c2c83"
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
{"42":"0x9fd66ee78a5ebe86006f12b37ff59c63f9caa15b","100":"0x95d3bd7d136bb0b7ac9988097e964236f8a9976e"}
|
|
@ -1 +0,0 @@
|
|||||||
{"42":"0xc270e6ea4fe303df9f1a3d4a132ac425264082e7","100":"0x7458dea485d9d8301e3ce43e8a1ec1456be5ba83"}
|
|
@ -1 +0,0 @@
|
|||||||
{"42":"0xf71ccf7ab48044ef9ae0b5e6983dbd3266b78b36","100":"0x3fc29fbe40c2d0ca78c7e81342f00226650fe2ad"}
|
|
4
lib/addresses/dao.json
Normal file
4
lib/addresses/dao.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"4": "0x95a7ce185efc2d1f13efd2a00ee9247c51ea7009",
|
||||||
|
"41787949": "0x183af3950364390a266edff2a0e7c4c2f95c0691"
|
||||||
|
}
|
13
lib/contracts/acl.js
Normal file
13
lib/contracts/acl.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const Base = require('./base');
|
||||||
|
const EthersUtils = require('ethers').utils;
|
||||||
|
|
||||||
|
class Acl extends Base {
|
||||||
|
|
||||||
|
hasPermission(fromAddress, contractAddress, roleID, params = null) {
|
||||||
|
let roleHash = EthersUtils.keccak256(EthersUtils.toUtf8Bytes(roleID));
|
||||||
|
console.log(roleHash)
|
||||||
|
return this.functions.hasPermission(fromAddress, contractAddress, roleHash, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Acl;
|
@ -17,11 +17,7 @@ class Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
on(type, callback) {
|
on(type, callback) {
|
||||||
let eventMethod = `on${type.toLowerCase()}`;
|
return this.contract.on(type, callback);
|
||||||
// Don't use this.contract.events here. Seems to be a bug in ethers.js
|
|
||||||
this.contract[eventMethod] = callback;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
module.exports = Base;
|
module.exports = Base;
|
||||||
|
70
lib/contracts/contribution.js
Normal file
70
lib/contracts/contribution.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
const ethers = require('ethers');
|
||||||
|
const RSVP = require('rsvp');
|
||||||
|
|
||||||
|
const ContributionSerializer = require('../serializers/contribution');
|
||||||
|
const Base = require('./base');
|
||||||
|
|
||||||
|
class Contribution extends Base {
|
||||||
|
all() {
|
||||||
|
return this.functions.contributionsCount()
|
||||||
|
.then((count) => {
|
||||||
|
count = count.toNumber();
|
||||||
|
let contributions = [];
|
||||||
|
|
||||||
|
for (let id = 1; id <= count; id++) {
|
||||||
|
contributions.push(this.getById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return RSVP.all(contributions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getById(id) {
|
||||||
|
id = ethers.utils.bigNumberify(id);
|
||||||
|
|
||||||
|
return this.functions.getContribution(id)
|
||||||
|
.then((data) => {
|
||||||
|
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getByContributor(contributor) {
|
||||||
|
return this.functions.balanceOf(contributor)
|
||||||
|
then((balance) => {
|
||||||
|
count = balance.toNumber();
|
||||||
|
|
||||||
|
let contributions = [];
|
||||||
|
|
||||||
|
for (let index = 0; index <= count; index++) {
|
||||||
|
this.functions.tokenOfOwnerByIndex(contributor, index)
|
||||||
|
.then((id) => {
|
||||||
|
contributions.push(this.getById(id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return RSVP.all(contributions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addContribution(contributionAttr, callOptions = {}) {
|
||||||
|
let json = ContributionSerializer.serialize(contributionAttr);
|
||||||
|
// TODO: validate against schema
|
||||||
|
|
||||||
|
return this.ipfs
|
||||||
|
.add(json)
|
||||||
|
.then((ipfsHashAttr) => {
|
||||||
|
let contribution = [
|
||||||
|
contributionAttr.amount,
|
||||||
|
contributionAttr.contributorAccount,
|
||||||
|
ipfsHashAttr.hashDigest,
|
||||||
|
ipfsHashAttr.hashFunction,
|
||||||
|
ipfsHashAttr.hashSize,
|
||||||
|
];
|
||||||
|
|
||||||
|
return this.functions.add(...contribution, callOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Contribution;
|
@ -1,6 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
Contributors: require('./contributor'),
|
Contributor: require('./contributor'),
|
||||||
Operator: require('./operator'),
|
Contribution: require('./contribution'),
|
||||||
|
Proposal: require('./proposal'),
|
||||||
Token: require('./token'),
|
Token: require('./token'),
|
||||||
Registry: require('./registry')
|
Kernel: require('./kernel'),
|
||||||
|
Acl: require('./acl')
|
||||||
};
|
};
|
||||||
|
24
lib/contracts/kernel.js
Normal file
24
lib/contracts/kernel.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const namehash = require('eth-ens-namehash').hash;
|
||||||
|
const Base = require('./base');
|
||||||
|
|
||||||
|
const KERNEL_APP_ADDR_NAMESPACE = '0xd6f028ca0e8edb4a8c9757ca4fdccab25fa1e0317da1188108f7d2dee14902fb';
|
||||||
|
|
||||||
|
class Kernel extends Base {
|
||||||
|
constructor(contract) {
|
||||||
|
super(contract);
|
||||||
|
this.apm = 'aragonpm.eth'; // can be overwritten if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
getApp(appName) {
|
||||||
|
if (appName === 'Acl') {
|
||||||
|
return this.functions.acl();
|
||||||
|
}
|
||||||
|
return this.functions.getApp(KERNEL_APP_ADDR_NAMESPACE, this.appNamehash(appName));
|
||||||
|
}
|
||||||
|
|
||||||
|
appNamehash(appName) {
|
||||||
|
return namehash(`kredits-${appName.toLowerCase()}.${this.apm}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Kernel;
|
@ -4,7 +4,7 @@ const RSVP = require('rsvp');
|
|||||||
const ContributionSerializer = require('../serializers/contribution');
|
const ContributionSerializer = require('../serializers/contribution');
|
||||||
const Base = require('./base');
|
const Base = require('./base');
|
||||||
|
|
||||||
class Operator extends Base {
|
class Proposal extends Base {
|
||||||
all() {
|
all() {
|
||||||
return this.functions.proposalsCount()
|
return this.functions.proposalsCount()
|
||||||
.then((count) => {
|
.then((count) => {
|
||||||
@ -23,12 +23,6 @@ class Operator extends Base {
|
|||||||
id = ethers.utils.bigNumberify(id);
|
id = ethers.utils.bigNumberify(id);
|
||||||
|
|
||||||
return this.functions.getProposal(id)
|
return this.functions.getProposal(id)
|
||||||
.then((data) => {
|
|
||||||
// TODO: remove when naming updated on the contract
|
|
||||||
data.hashDigest = data.ipfsHash;
|
|
||||||
return data;
|
|
||||||
})
|
|
||||||
// Fetch IPFS data if available
|
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
return this.ipfs.catAndMerge(data, ContributionSerializer.deserialize);
|
||||||
});
|
});
|
||||||
@ -54,4 +48,4 @@ class Operator extends Base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Operator;
|
module.exports = Proposal
|
@ -1,6 +0,0 @@
|
|||||||
const Base = require('./base');
|
|
||||||
|
|
||||||
class Registry extends Base {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Registry;
|
|
@ -4,12 +4,21 @@ const RSVP = require('rsvp');
|
|||||||
const Preflight = require('./utils/preflight');
|
const Preflight = require('./utils/preflight');
|
||||||
|
|
||||||
const ABIS = {
|
const ABIS = {
|
||||||
Contributors: require('./abis/Contributors.json'),
|
Contributor: require('./abis/Contributor.json'),
|
||||||
Operator: require('./abis/Operator.json'),
|
Contribution: require('./abis/Contribution.json'),
|
||||||
Registry: require('./abis/Registry.json'),
|
Token: require('./abis/Token.json'),
|
||||||
Token: require('./abis/Token.json')
|
Proposal: require('./abis/Proposal.json'),
|
||||||
|
Kernel: require('./abis/Kernel.json'),
|
||||||
|
Acl: require('./abis/ACL.json')
|
||||||
};
|
};
|
||||||
const RegistryAddress = require('./addresses/Registry.json');
|
const APP_CONTRACTS = [
|
||||||
|
'Contributor',
|
||||||
|
'Contribution',
|
||||||
|
'Token',
|
||||||
|
'Proposal',
|
||||||
|
'Acl'
|
||||||
|
];
|
||||||
|
const DaoAddresses = require('./addresses/dao.json');
|
||||||
|
|
||||||
const Contracts = require('./contracts');
|
const Contracts = require('./contracts');
|
||||||
const IPFS = require('./utils/ipfs')
|
const IPFS = require('./utils/ipfs')
|
||||||
@ -27,26 +36,29 @@ class Kredits {
|
|||||||
|
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.signer = signer;
|
this.signer = signer;
|
||||||
// by default we only need the registry address.
|
this.options = options;
|
||||||
// the rest is loaded from there in the init() function
|
this.addresses = addresses || {};
|
||||||
this.addresses = addresses || { Registry: RegistryAddress[this.provider.chainId.toString()] }; // chainID must be a string
|
|
||||||
this.abis = abis || ABIS;
|
this.abis = abis || ABIS;
|
||||||
this.ipfs = new IPFS(ipfsConfig);
|
this.ipfs = new IPFS(ipfsConfig);
|
||||||
this.contracts = {};
|
this.contracts = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
init(names) {
|
init(names) {
|
||||||
let contractsToLoad = names || Object.keys(ABIS);
|
let contractsToLoad = names || APP_CONTRACTS;
|
||||||
|
return this.provider.getNetwork().then(network => {
|
||||||
|
this.addresses['Kernel'] = this.addresses['Kernel'] || DaoAddresses[network.chainId.toString()];
|
||||||
let addressPromises = contractsToLoad.map((contractName) => {
|
let addressPromises = contractsToLoad.map((contractName) => {
|
||||||
return this.Registry.functions.getProxyFor(contractName).then((address) => {
|
return this.Kernel.getApp(contractName).then((address) => {
|
||||||
this.addresses[contractName] = address;
|
this.addresses[contractName] = address;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
throw new Error(`Failed to get address for ${contractName} from registry at ${this.Registry.contract.address}
|
console.log(error);
|
||||||
- correct registry? does it have version entry? - ${error.message}`
|
throw new Error(`Failed to get address for ${contractName} from DAO at ${this.Kernel.contract.address}
|
||||||
|
- ${error.message}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return RSVP.all(addressPromises).then(() => { return this });
|
return RSVP.all(addressPromises).then(() => { return this });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static setup(provider, signer, ipfsConfig = null) {
|
static setup(provider, signer, ipfsConfig = null) {
|
||||||
@ -54,13 +66,17 @@ class Kredits {
|
|||||||
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
|
return new Kredits(provider, signer, { ipfsConfig: ipfsConfig }).init();
|
||||||
}
|
}
|
||||||
|
|
||||||
get Registry() {
|
get Kernel() {
|
||||||
return this.contractFor('registry');
|
let k = this.contractFor('Kernel');
|
||||||
|
// in case we want to use a special apm (e.g. development vs. production)
|
||||||
|
if (this.options.apm) {
|
||||||
|
k.apm = this.options.apm;
|
||||||
|
}
|
||||||
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contributor() {
|
get Contributor() {
|
||||||
// TODO: rename to contributor
|
return this.contractFor('Contributor');
|
||||||
return this.contractFor('contributors');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get Contributors() {
|
get Contributors() {
|
||||||
@ -68,12 +84,24 @@ class Kredits {
|
|||||||
return this.Contributor;
|
return this.Contributor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get Proposal() {
|
||||||
|
return this.contractFor('Proposal');
|
||||||
|
}
|
||||||
|
|
||||||
get Operator() {
|
get Operator() {
|
||||||
return this.contractFor('operator');
|
return this.Proposal;
|
||||||
}
|
}
|
||||||
|
|
||||||
get Token() {
|
get Token() {
|
||||||
return this.contractFor('token');
|
return this.contractFor('Token');
|
||||||
|
}
|
||||||
|
|
||||||
|
get Contribution() {
|
||||||
|
return this.contractFor('Contribution');
|
||||||
|
}
|
||||||
|
|
||||||
|
get Acl() {
|
||||||
|
return this.contractFor('Acl');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be private
|
// Should be private
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
var Registry = artifacts.require('./upgradeable/Registry.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Registry);
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Token = artifacts.require('./Token.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Token).then(function(token) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Token address: ', Token.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Token', Token.address);
|
|
||||||
registry.createProxy('Token', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Contributors = artifacts.require('./Contributors.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Contributors).then(function(contributors) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Contributors address: ', Contributors.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Contributors', Contributors.address);
|
|
||||||
registry.createProxy('Contributors', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
var Registry = artifacts.require('./Registry.sol');
|
|
||||||
var Operator = artifacts.require('./Operator.sol');
|
|
||||||
|
|
||||||
module.exports = function(deployer) {
|
|
||||||
deployer.deploy(Operator).then(function(operator) {
|
|
||||||
console.log('Registry address: ', Registry.address);
|
|
||||||
console.log('Operator address: ', Operator.address);
|
|
||||||
Registry.deployed().then(function(registry) {
|
|
||||||
registry.addVersion('Operator', Operator.address);
|
|
||||||
registry.createProxy('Operator', 1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
14837
package-lock.json
generated
14837
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user