Skip to content

Commit

Permalink
Merge pull request #171 from kyledewy/master
Browse files Browse the repository at this point in the history
Fixed governance test
  • Loading branch information
0xdewy committed Oct 24, 2018
2 parents a6c8156 + 7bab9d6 commit 95819e5
Show file tree
Hide file tree
Showing 184 changed files with 40,963 additions and 3,863 deletions.
25 changes: 8 additions & 17 deletions README.md
Expand Up @@ -48,35 +48,26 @@ yarn coverage
```
## [Roles](contracts/roles)
To understand the SDK, it's important to understand the 4 fundamental roles on the network:
There are generally 4 different roles on the platform. The `Investor`, the `AssetManager`, the `Operator`, and the `PlatformOwners`. `Investors` can contribute ETH or Erc20 tokens to invest in new asset crowdsales. The continued functioning of the asset is ensured by the `AssetManager`, who receives a fee for his work and escrows tokens as collateral to investors. The `Operator` receives funds from the crowdsale and produces and installs the asset. `PlatformOwners` can choose how assets are governed, and whether or not a contract upgrade should happen. The platform owner can be a single account or a contract governed by many accounts.
#### Investor
Investors can contribute ETH or ERC20 tokens to invest in asset orders, receiving asset-tokens in return. To invest they must approve the burner contract.
#### AssetManager
The assets are managed and created by the AssetManager, who receives a fee for his work and escrows tokens as collateral to investors. The AssetManager must approve the burner contract to burn platform tokens.
#### Operator
The Operator is responsible for producing and installing the asset. They receive 99% of the crowdsale income as a fee for doing so. Platform owners must approve each Operator.
#### Platform Owner(s)
Platform owners can choose how assets are governed, and whether or not a contract upgrade should happen. The platform owner can be a single account or a contract governed by many accounts. Platform owners receive 1% of crowdsale income as a fee.
## Setting Up The Platform
## Contract overview
Before creating assets, certain variables and parameters have to be set:
* All contracts must be registered in ContractManager before writing to database
* All users must approve the current contract state, which changes everytime a contract is added/updated in ContractManager
* Users must approve ERC20Burner to burn platform tokens before using key functionality
* Platform wallet and platform token must be set
* Operators must be registered and choose which currencies they wish to accept
Basic functionality for these critical operations are outlined below. All contracts are found [here](contracts)
Basic functionality for these critical operations are outlined below.
All contracts are found [here](contracts)
### [Database](contracts/database)
Contracts in the SDK store all long-term data in a database contract, which allows for contracts to be upgraded without losing valuable data. The Database stores all data using a bytes32 type, which is often the keccak256 hash of the variableName, ID, address that make up that variable.
Storing an unsigned integer looks like this:
Storing an integer looks like this:
```javascript
database.setUint(keccak256(abi.encodePacked("fundingDeadline", assetID)), 20000000);
```
Expand Down Expand Up @@ -121,7 +112,7 @@ To give a contract write access to the database, you must call `addContract(cont
}
```

Everytime a contract is added or updated the contract state will change, requiring approval from users before they interact with the platform. Users can also choose to ignore future state changes. This can be done by calling the following function:
Every time a contract is added or updated the contract state will change, requiring approval from users before they interact with the platform. Users can also choose to ignore future state changes. This can be done by calling the following function:

```javascript
function setContractStatePreferences(bool _acceptCurrentState, bool _ignoreStateChanges)
Expand Down Expand Up @@ -180,7 +171,7 @@ Before assets can be funded the platform owners must set the `platform token` an
emit LogPlatformWallet(_walletAddress);
}
```
:heavy_plus_sign:
AND

```javascript
// @notice
Expand Down
20 changes: 10 additions & 10 deletions accounts.json
@@ -1,12 +1,12 @@
[
"0xeed17b217b1e0c63b1b525d8d2e59cad0546432d",
"0x0ec0e59bd4faf0a136c2ee638f3fcff19972a304",
"0xe273d3d13d69f82b8071365e110e13c0fbde0c59",
"0x4cbe9306228ff003fe5a47fe66f2e3d3162f0f37",
"0x0dcea99d96046de44f1ab2a4bf9b3e21f3ac2924",
"0x8bd581cca82af7cfff8ab46591803fe2f2061218",
"0xd6a41e3da4bdc4a27d5707b08612925347f5934f",
"0xd0702a4258c574ff15df4e65000dbb16843cbd24",
"0xc3dda1fc00ba83300b9ad2d00faa30166fabc657",
"0x45543e02e26442e6c95e3145f7d30a53b284f418"
"0x83ab44a76fc2e7238fca811e38e41845ed1a9fb3",
"0xbcccfb280ff402bc5a593dfce2b3dc736e17e7ba",
"0x6d929ebae40bbf96f52e3f82bd831c2891a0d75d",
"0x9916ebeef94abca7564ce7dabcbacd434f4015cd",
"0xf89f0b05980fb8c7b3d985206707f20a6189c5de",
"0x87460046866e93e99273a9f8a94d1e5764774dc9",
"0x65a67881065abe61de49c13c44f55a7916c252ae",
"0x557f883a3a7b4609ca1251c60253c79e275ba1a6",
"0x310ddb0d62b77fb195dcaebfadcebaf3978c198f",
"0x4449f9fcc41cf2275fd3631e044e3cf9d603efc2"
]
32 changes: 16 additions & 16 deletions addresses.json
@@ -1,18 +1,18 @@
{
"MyBit": "0x82e8ed40b49fa4682d58807e99eaed6daf20b1d4",
"ERC20Burner": "0xfa3066aa522b18e0243f7c098e9c72fa85c89ae2",
"Database": "0xf3a985536aa63fcc5e0732fef35a9d6e0881682c",
"ContractManager": "0x8397e224cc3f2560d6c989bc29bb6800517d6f97",
"API": "0x94e839af9b8ec45da92d8a76e0acd1ca45b710c5",
"SingleOwned": "0x6df2e96b18b909e6127137a4a8ddf40eb259b610",
"Pausible": "0xbea6ba1b1aa691fc07a5b722bc491df03f4c280a",
"AccessHierarchy": "0xe4f4aeefd5087eababbd73bdd4c8188cbd008ebb",
"PlatformFunds": "0x7bd53d17ebee6c87d9937c8cb4fb04297cdc83c2",
"Operators": "0x54ae7cc1c7ec065a84b84ea7f9e33a14cf3d95e6",
"AssetManagerEscrow": "0x860b672a9fcb0f12c156cc9c233cfc3ed18fc202",
"CrowdsaleETH": "0x8af6ac104c639a9011b83f3baf789271e6df6198",
"CrowdsaleGeneratorETH": "0xeed31156aa0eeb2ebf473cd16faf70d2bf463137",
"CrowdsaleERC20": "0x03a685558ffd806ca2c44ab5f2ebe84688b82d57",
"CrowdsaleGeneratorERC20": "0xede40300bea9631bddbf1ab91ca286b5cd591d14",
"AssetExchange": "0x57bff4f318c5fb88e742d89627ad0b3ee62ab536"
"MyBit": "0x8a36b3e2b97fceadb96f42975f6b2fd5af1cadd2",
"ERC20Burner": "0xedc8486aebbaeed27a2d14a0cb5f01586828cff4",
"Database": "0xacabd45a26baf228a6f753f63c75730edc377cea",
"ContractManager": "0xaa0a3786966d598686ba95897577b5443e76f4f3",
"API": "0x02ee482c286dfd1ae43d3bd3315dec0f6ee4ee6f",
"SingleOwned": "0x8e43584cbfdfe8a646b901363c810087574594cb",
"Pausible": "0xd1f82620bbd1f8eaa329c3250b6edc1db9515ae3",
"AccessHierarchy": "0xd96a07f8e1c81b5fec25d5b49a92030a2dfcab6f",
"PlatformFunds": "0xc01dc119cc3791a7e0cf2dd08eef3e143577794c",
"Operators": "0xdb17f66f6d536b092a72177311c45a31e59bc8e5",
"AssetManagerEscrow": "0x96e5fafbb47ff3a9f9d261d549b720e9f2120b84",
"CrowdsaleETH": "0xb85c21e4afdaa416c068ebf1e74e24e7a2e446c2",
"CrowdsaleGeneratorETH": "0x3e1553ae089cf495418786a5ffeb314bcdfef205",
"CrowdsaleERC20": "0xacd78e2db9c77e2242f74d47438c5824cf6e02ce",
"CrowdsaleGeneratorERC20": "0x1d96b9640ef47e5e8e6161bf30a1cb798ab9e7ef",
"AssetExchange": "0xf93210ea71bbd541b2362c4e1b1ff3ff5c3cfb5c"
}
3 changes: 2 additions & 1 deletion contracts/database/API.sol
Expand Up @@ -27,6 +27,7 @@ contract API {
using SafeMath for uint256;

DBView private database;
uint constant scalingFactor = 10e32;

constructor(address _database)
public {
Expand Down Expand Up @@ -69,7 +70,7 @@ contract API {
view
returns (uint) {
uint totalVotes = getTotalVotes(_executionID);
return (totalVotes * 100) / TokenView(_assetToken).totalSupply();
return ( ( (totalVotes * 100) * scalingFactor) / TokenView(_assetToken).totalSupply() / scalingFactor);
}

function getAssetManagerParameterHash(bytes32 _assetID, address _oldAssetManager, address _newAssetManager, uint _amount, bool _burn)
Expand Down
17 changes: 7 additions & 10 deletions contracts/ownership/AssetGovernance.sol
Expand Up @@ -21,6 +21,8 @@ contract AssetGovernance {

DB public database;

uint public consensus = 66; // TODO: sub the assetmanager portion of tokens, since they can't be voted with
uint constant scalingFactor = 10e32;

constructor(address _database)
public {
Expand All @@ -39,7 +41,7 @@ contract AssetGovernance {
bytes32 investorVotesID = keccak256(abi.encodePacked("investorVotes", executionID, msg.sender));
uint256 numVotes = database.uintStorage(numVotesID);
uint256 investorVotes = database.uintStorage(investorVotesID);
require(lockTokens(_assetID, msg.sender, _amountToLock));
require(lockTokens(_assetID, msg.sender, _amountToLock), "unable to lock tokens");
database.setUint(numVotesID, numVotes.add(_amountToLock));
database.setUint(investorVotesID, investorVotes.add(_amountToLock));
return true;
Expand All @@ -54,7 +56,7 @@ contract AssetGovernance {
returns (bool) {
bytes32 executionID = keccak256(abi.encodePacked(_executingContract, _assetID, _methodID, _parameterHash));
bytes32 voteTotalID = keccak256(abi.encodePacked("voteTotal", executionID));
bytes32 investorVotesID = keccak256(abi.encodePacked("investorVotes", executionID));
bytes32 investorVotesID = keccak256(abi.encodePacked("investorVotes", executionID, msg.sender));
uint investorVotes = database.uintStorage(investorVotesID);
uint totalVotes = database.uintStorage(voteTotalID);
require(investorVotes <= _amountToUnlock); // 1 vote = 1 token
Expand All @@ -67,7 +69,7 @@ contract AssetGovernance {
// Public Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// @notice Checks that 1/3 or more of token holders agreed on function call
// @notice Checks that 2/3 or more of token holders agreed on function call
function isConsensusReached(address _executingContract, bytes32 _assetID, bytes4 _methodID, bytes32 _parameterHash)
public
view
Expand All @@ -76,7 +78,7 @@ contract AssetGovernance {
bytes32 executionID = keccak256(abi.encodePacked(_executingContract, _assetID, _methodID, _parameterHash));
bytes32 numVotesID = keccak256(abi.encodePacked("voteTotal", executionID));
uint256 numTokens = assetToken.totalSupply();
return database.uintStorage(numVotesID).mul(100).div(numTokens) >= 33;
return database.uintStorage(numVotesID).mul(scalingFactor).mul(100).div(numTokens).div(scalingFactor) >= consensus;
}


Expand All @@ -101,17 +103,12 @@ contract AssetGovernance {
// Modifiers
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// @notice add this modifer to functions that you want multi-sig requirements for
// @dev function can only be called after at least n >= quorumLevel owners have agreed to call it
modifier hasConsensus(bytes32 _assetID, bytes4 _methodID, bytes32 _parameterHash) {
require(isConsensusReached(address(this), _assetID, _methodID, _parameterHash)); // owners must have agreed on function + parameters
_;
}

// @notice reverts if the asset does not have a token address set in the database
modifier validAsset(bytes32 _assetID) {
require(database.addressStorage(keccak256(abi.encodePacked("tokenAddress", _assetID))) != address(0));
_;
}

event LogConsensus(bytes32 votesID, uint votes, uint tokens, bytes32 executionID, uint quorum);
}
42 changes: 27 additions & 15 deletions contracts/roles/AssetManagerEscrow.sol
Expand Up @@ -15,6 +15,8 @@

DBInterface public database;

uint public consensus = 66;

// @notice constructor: initializes database
// @param: the address for the database contract used by this platform
constructor(address _database)
Expand All @@ -27,17 +29,12 @@
function lockEscrow(bytes32 _assetID, uint _amount)
public
returns (bool) {
require(database.addressStorage(keccak256(abi.encodePacked("assetManager", _assetID))) == address(0));
bytes32 assetManagerEscrowID = keccak256(abi.encodePacked(_assetID, msg.sender));
address tokenAddress = database.addressStorage(keccak256(abi.encodePacked("platformToken")));
require(BurnableERC20(tokenAddress).transferFrom(msg.sender, address(this), _amount));
database.setUint(keccak256(abi.encodePacked("assetManagerEscrow", assetManagerEscrowID)), _amount);
database.setAddress(keccak256(abi.encodePacked("assetManager", _assetID)), msg.sender);
emit LogEscrowLocked(_assetID, assetManagerEscrowID, msg.sender, _amount);
require(lockEscrowInternal(msg.sender, _assetID, _amount));
return true;
}



// @notice assetManager can unlock his escrow here once funding fails or asset returns sufficient ROI
// @dev asset must have fundingDeadline = 0 or have ROI > 25%
// @dev returns escrow according to ROI. 25% ROI returns 25% of escrow, 50% ROI returns 50% of escrow etc...
Expand Down Expand Up @@ -73,23 +70,22 @@

// @notice investors can vote to call this function for the new assetManager to then call
// @dev new assetManager must approve this contract to transfer in and lock _ amount of platform tokens
function becomeAssetManager(bytes32 _assetID, address _oldAssetManager, uint _amount, bool _burn)
function becomeAssetManager(bytes32 _assetID, address _oldAssetManager, uint256 _amount, bool _burn)
external
hasConsensus(_assetID, msg.sig, keccak256(abi.encodePacked(_assetID, _oldAssetManager, msg.sender, _amount, _burn)))
returns (bool) {
address currentAssetManager = database.addressStorage(keccak256(abi.encodePacked("assetManager", _assetID)));
require(currentAssetManager != msg.sender && currentAssetManager == _oldAssetManager);
bytes32 assetManagerEscrowID = keccak256(abi.encodePacked(_assetID, _oldAssetManager));
uint oldEscrowRemaining = database.uintStorage(keccak256(abi.encodePacked("assetManagerEscrow", assetManagerEscrowID))).sub(database.uintStorage(keccak256(abi.encodePacked("escrowRedeemed", assetManagerEscrowID))));
bytes32 oldAssetManagerEscrowID = keccak256(abi.encodePacked(_assetID, _oldAssetManager));
uint oldEscrowRemaining = database.uintStorage(keccak256(abi.encodePacked("assetManagerEscrow", oldAssetManagerEscrowID))).sub(database.uintStorage(keccak256(abi.encodePacked("escrowRedeemed", oldAssetManagerEscrowID))));
BurnableERC20 token = BurnableERC20(database.addressStorage(keccak256(abi.encodePacked("platformToken"))));
require(removeAssetManager(_assetID, assetManagerEscrowID));
require(removeAssetManager(_assetID, oldAssetManagerEscrowID));
if (_burn) { require(token.burn(oldEscrowRemaining)); }
else { require(token.transfer(_oldAssetManager, oldEscrowRemaining)); }
require(lockEscrow(_assetID, _amount));
require(lockEscrowInternal(msg.sender, _assetID, _amount));
return true;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal Functions
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -105,6 +101,21 @@
}


function lockEscrowInternal(address _assetManager, bytes32 _assetID, uint _amount)
internal
returns (bool) {
require(database.addressStorage(keccak256(abi.encodePacked("assetManager", _assetID))) == address(0));
bytes32 assetManagerEscrowID = keccak256(abi.encodePacked(_assetID, _assetManager));
address tokenAddress = database.addressStorage(keccak256(abi.encodePacked("platformToken")));
require(BurnableERC20(tokenAddress).transferFrom(_assetManager, address(this), _amount));
database.setUint(keccak256(abi.encodePacked("assetManagerEscrow", assetManagerEscrowID)), _amount);
database.setAddress(keccak256(abi.encodePacked("assetManager", _assetID)), _assetManager);
emit LogEscrowLocked(_assetID, assetManagerEscrowID, _assetManager, _amount);
return true;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Modifiers
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -114,11 +125,12 @@
modifier hasConsensus(bytes32 _assetID, bytes4 _methodID, bytes32 _parameterHash) {
bytes32 numVotesID = keccak256(abi.encodePacked("voteTotal", keccak256(abi.encodePacked(address(this), _assetID, _methodID, _parameterHash))));
uint256 numTokens = DivToken(database.addressStorage(keccak256(abi.encodePacked("tokenAddress", _assetID)))).totalSupply();
require(database.uintStorage(numVotesID).mul(100).div(numTokens) >= 33);
emit LogConsensus(numVotesID, database.uintStorage(numVotesID), numTokens, keccak256(abi.encodePacked(address(this), _assetID, _methodID, _parameterHash)), database.uintStorage(numVotesID).mul(100).div(numTokens));
require(database.uintStorage(numVotesID).mul(100).div(numTokens) >= consensus, 'Consensus not reached');
_;
}


event LogConsensus(bytes32 votesID, uint votes, uint tokens, bytes32 executionID, uint quorum);
event LogEscrowBurned(bytes32 indexed _assetID, address indexed _assetManager, uint _amountBurnt);
event LogEscrowLocked(bytes32 indexed _assetID, bytes32 indexed _assetManagerEscrowID, address indexed _assetManager, uint _amount);

Expand Down
1 change: 1 addition & 0 deletions coverage.json

Large diffs are not rendered by default.

0 comments on commit 95819e5

Please sign in to comment.