2017-06-06 17:40:14 +00:00
pragma solidity ^ 0 . 4 . 11 ;
2017-09-13 12:41:08 +00:00
import " ./ILiquidPledgingPlugin.sol " ;
2017-09-29 10:11:13 +00:00
/// @dev This is declares a few functions from `Vault` so that the
/// `LiquidPledgingBase` contract can interface with the `Vault` contract
2017-07-13 17:12:45 +00:00
contract Vault {
function authorizePayment ( bytes32 _ref , address _dest , uint _amount ) ;
2017-08-13 11:23:00 +00:00
function ( ) payable ;
2017-07-13 17:12:45 +00:00
}
2017-06-06 17:40:14 +00:00
2017-06-26 17:54:28 +00:00
contract LiquidPledgingBase {
2017-09-29 10:11:13 +00:00
// Limits inserted to prevent large loops that could prevent canceling
2017-07-13 17:21:53 +00:00
uint constant MAX_DELEGATES = 20 ;
2017-07-09 17:04:02 +00:00
uint constant MAX_SUBPROJECT_LEVEL = 20 ;
2017-08-18 16:47:22 +00:00
uint constant MAX_INTERPROJECT_LEVEL = 20 ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
enum NoteManagerType { Donor , Delegate , Project } // todo change name Donor Project
enum PaymentState { NotPaid , Paying , Paid } // TODO name change NotPaid
/// @dev This struct defines the details of each the NoteManager, these
/// NoteManagers can own notes and act as delegates
struct NoteManager { // TODO name change NoteManager
NoteManagerType managerType ; // Giver, Delegate or Campaign
address addr ; // account or contract address for admin
string name ;
uint64 commitTime ; // In seconds, used for Givers' & Delegates' vetos
uint64 parentProject ; // Only for campaigns
bool canceled ; //Always false except for canceled campaigns
ILiquidPledgingPlugin plugin ; // if the plugin is 0x0 then nothing happens if its a contract address than that smart contract is called via the milestone contract
2017-06-06 17:40:14 +00:00
}
struct Note {
uint amount ;
2017-09-29 10:11:13 +00:00
uint64 owner ; //NoteManager
uint64 [ ] delegationChain ; // list of index numbers
2017-07-09 17:04:02 +00:00
uint64 proposedProject ; // TODO change the name only used for when delegates are precommiting to a project
2017-09-29 10:11:13 +00:00
uint64 commitTime ; // When the proposedProject will become the owner
2017-07-13 17:21:53 +00:00
uint64 oldNote ; // this points to the Note[] index that the Note was derived from
2017-06-06 17:40:14 +00:00
PaymentState paymentState ;
}
Note [ ] notes ;
2017-09-29 10:11:13 +00:00
NoteManager [ ] managers ; //The list of noteManagers 0 means there is no manager
2017-06-06 17:40:14 +00:00
Vault public vault ;
2017-07-13 17:21:53 +00:00
// this mapping allows you to search for a specific note's index number by the hash of that note
mapping ( bytes32 => uint64 ) hNote2ddx ; //TODO Fix typo
2017-06-06 17:40:14 +00:00
/////
// Modifiers
/////
modifier onlyVault ( ) {
2017-07-13 17:12:45 +00:00
require ( msg . sender == address ( vault ) ) ;
2017-06-06 17:40:14 +00:00
_ ;
}
//////
// Constructor
//////
2017-09-29 10:11:13 +00:00
/// @notice The Constructor creates the `LiquidPledgingBase` on the blockchain
/// @param _vault Where the ETH is stored that the pledges represent
2017-06-06 17:40:14 +00:00
function LiquidPledgingBase ( address _vault ) {
2017-07-09 17:04:02 +00:00
managers . length = 1 ; // we reserve the 0 manager
notes . length = 1 ; // we reserve the 0 note
2017-06-06 17:40:14 +00:00
vault = Vault ( _vault ) ;
}
///////
// Managers functions
//////
2017-09-29 10:11:13 +00:00
/// @notice Creates a donor.
function addDonor ( string name , uint64 commitTime , ILiquidPledgingPlugin plugin
2017-10-03 09:36:14 +00:00
) returns ( uint64 idDonor ) {
2017-09-14 18:06:58 +00:00
idDonor = uint64 ( managers . length ) ;
2017-06-06 17:40:14 +00:00
managers . push ( NoteManager (
NoteManagerType . Donor ,
msg . sender ,
name ,
commitTime ,
2017-08-18 16:47:22 +00:00
0 ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-09-28 15:49:10 +00:00
2017-09-14 18:06:58 +00:00
DonorAdded ( idDonor ) ;
2017-06-06 17:40:14 +00:00
}
2017-07-09 17:04:02 +00:00
event DonorAdded ( uint64 indexed idDonor ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
///@notice Changes the address, name or commitTime associated with a specific donor
2017-06-06 17:40:14 +00:00
function updateDonor (
uint64 idDonor ,
address newAddr ,
string newName ,
uint64 newCommitTime )
{
2017-07-13 17:12:45 +00:00
NoteManager storage donor = findManager ( idDonor ) ;
2017-09-29 10:11:13 +00:00
require ( donor . managerType == NoteManagerType . Donor ) ; //Must be a Giver
require ( donor . addr == msg . sender ) ; //current addr had to originate this tx
2017-06-06 17:40:14 +00:00
donor . addr = newAddr ;
donor . name = newName ;
donor . commitTime = newCommitTime ;
DonorUpdated ( idDonor ) ;
}
2017-07-09 17:04:02 +00:00
event DonorUpdated ( uint64 indexed idDonor ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
/// @notice Creates a new Delegate
2017-09-14 18:06:58 +00:00
function addDelegate ( string name , uint64 commitTime , ILiquidPledgingPlugin plugin ) returns ( uint64 idDelegate ) { //TODO return index number
idDelegate = uint64 ( managers . length ) ;
2017-06-06 17:40:14 +00:00
managers . push ( NoteManager (
NoteManagerType . Delegate ,
msg . sender ,
name ,
2017-09-13 12:41:08 +00:00
commitTime ,
2017-06-06 17:40:14 +00:00
0 ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-06-06 17:40:14 +00:00
2017-09-28 15:50:57 +00:00
DelegateAdded ( idDelegate ) ;
2017-06-06 17:40:14 +00:00
}
2017-09-28 15:49:10 +00:00
event DelegateAdded ( uint64 indexed idDelegate ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
///@notice Changes the address, name or commitTime associated with a specific delegate
2017-09-13 12:41:08 +00:00
function updateDelegate (
uint64 idDelegate ,
address newAddr ,
string newName ,
2017-09-18 13:43:09 +00:00
uint64 newCommitTime ) {
2017-07-13 17:12:45 +00:00
NoteManager storage delegate = findManager ( idDelegate ) ;
require ( delegate . managerType == NoteManagerType . Delegate ) ;
require ( delegate . addr == msg . sender ) ;
2017-06-06 17:40:14 +00:00
delegate . addr = newAddr ;
delegate . name = newName ;
2017-09-13 12:41:08 +00:00
delegate . commitTime = newCommitTime ;
2017-06-06 17:40:14 +00:00
DelegateUpdated ( idDelegate ) ;
}
2017-07-09 17:04:02 +00:00
event DelegateUpdated ( uint64 indexed idDelegate ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
/// @notice Creates a new Campaign
2017-09-14 18:06:58 +00:00
function addProject ( string name , address projectManager , uint64 parentProject , uint64 commitTime , ILiquidPledgingPlugin plugin ) returns ( uint64 idProject ) {
2017-08-18 16:47:22 +00:00
if ( parentProject != 0 ) {
NoteManager storage pm = findManager ( parentProject ) ;
require ( pm . managerType == NoteManagerType . Project ) ;
require ( pm . addr == msg . sender ) ;
require ( getProjectLevel ( pm ) < MAX_SUBPROJECT_LEVEL ) ;
}
2017-09-14 18:06:58 +00:00
idProject = uint64 ( managers . length ) ;
2017-06-06 17:40:14 +00:00
managers . push ( NoteManager (
NoteManagerType . Project ,
2017-08-18 16:47:22 +00:00
projectManager ,
2017-06-06 17:40:14 +00:00
name ,
commitTime ,
2017-08-18 16:47:22 +00:00
parentProject ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-06-06 17:40:14 +00:00
2017-09-14 18:06:58 +00:00
ProjectAdded ( idProject ) ;
2017-06-06 17:40:14 +00:00
}
2017-07-09 17:04:02 +00:00
event ProjectAdded ( uint64 indexed idProject ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
///@notice Changes the address, name or commitTime associated with a specific Campaign
2017-09-18 13:43:09 +00:00
function updateProject (
uint64 idProject ,
address newAddr ,
string newName ,
uint64 newCommitTime )
{
2017-07-13 17:12:45 +00:00
NoteManager storage project = findManager ( idProject ) ;
require ( project . managerType == NoteManagerType . Project ) ;
require ( project . addr == msg . sender ) ;
2017-06-06 17:40:14 +00:00
project . addr = newAddr ;
project . name = newName ;
project . commitTime = newCommitTime ;
ProjectUpdated ( idProject ) ;
}
2017-07-09 17:04:02 +00:00
event ProjectUpdated ( uint64 indexed idManager ) ;
2017-06-06 17:40:14 +00:00
//////////
// Public constant functions
//////////
2017-09-29 10:11:13 +00:00
/// @notice Public constant that states how many notes are in the system
2017-06-06 17:40:14 +00:00
function numberOfNotes ( ) constant returns ( uint ) {
return notes . length - 1 ;
}
2017-09-29 10:11:13 +00:00
/// @notice Public constant that states the details of the specified Note
2017-06-06 17:40:14 +00:00
function getNote ( uint64 idNote ) constant returns (
uint amount ,
uint64 owner ,
uint64 nDelegates ,
uint64 proposedProject ,
2017-06-27 11:08:23 +00:00
uint64 commitTime ,
2017-06-06 17:40:14 +00:00
uint64 oldNote ,
PaymentState paymentState
) {
2017-07-13 17:12:45 +00:00
Note storage n = findNote ( idNote ) ;
2017-06-06 17:40:14 +00:00
amount = n . amount ;
owner = n . owner ;
nDelegates = uint64 ( n . delegationChain . length ) ;
proposedProject = n . proposedProject ;
2017-06-27 11:08:23 +00:00
commitTime = n . commitTime ;
2017-06-06 17:40:14 +00:00
oldNote = n . oldNote ;
paymentState = n . paymentState ;
}
2017-09-29 10:11:13 +00:00
/// @notice Public constant that states the delegates one by one, because
/// an array cannot be returned
2017-06-06 17:40:14 +00:00
function getNoteDelegate ( uint64 idNote , uint idxDelegate ) constant returns (
uint64 idDelegate ,
address addr ,
string name
) {
2017-07-13 17:12:45 +00:00
Note storage n = findNote ( idNote ) ;
2017-06-26 17:54:28 +00:00
idDelegate = n . delegationChain [ idxDelegate - 1 ] ;
2017-07-13 17:12:45 +00:00
NoteManager storage delegate = findManager ( idDelegate ) ;
2017-06-06 17:40:14 +00:00
addr = delegate . addr ;
name = delegate . name ;
}
2017-09-29 10:11:13 +00:00
/// @notice Public constant that states the number of admins in the system
2017-06-26 17:54:28 +00:00
function numberOfNoteManagers ( ) constant returns ( uint ) {
2017-06-06 17:40:14 +00:00
return managers . length - 1 ;
}
2017-09-29 10:11:13 +00:00
/// @notice Public constant that states the details of the specified admin
2017-06-06 17:40:14 +00:00
function getNoteManager ( uint64 idManager ) constant returns (
NoteManagerType managerType ,
address addr ,
string name ,
uint64 commitTime ,
2017-08-18 16:47:22 +00:00
uint64 parentProject ,
2017-09-26 19:06:45 +00:00
bool canceled ,
address plugin )
2017-06-06 17:40:14 +00:00
{
2017-07-13 17:12:45 +00:00
NoteManager storage m = findManager ( idManager ) ;
2017-06-06 17:40:14 +00:00
managerType = m . managerType ;
addr = m . addr ;
name = m . name ;
commitTime = m . commitTime ;
2017-08-18 16:47:22 +00:00
parentProject = m . parentProject ;
2017-06-06 17:40:14 +00:00
canceled = m . canceled ;
2017-09-26 19:26:26 +00:00
plugin = address ( m . plugin ) ;
2017-06-06 17:40:14 +00:00
}
////////
// Private methods
///////
2017-09-29 10:11:13 +00:00
/// @notice All notes technically exist... but if the note hasn't been
/// created in this system yet then it wouldn't be in the hash array
/// hNoteddx[]; this creates a Pledge with and amount of 0 if one is not
/// created already...
2017-06-06 17:40:14 +00:00
function findNote (
uint64 owner ,
uint64 [ ] delegationChain ,
uint64 proposedProject ,
2017-06-27 11:08:23 +00:00
uint64 commitTime ,
2017-06-06 17:40:14 +00:00
uint64 oldNote ,
PaymentState paid
) internal returns ( uint64 )
{
2017-06-27 11:08:23 +00:00
bytes32 hNote = sha3 ( owner , delegationChain , proposedProject , commitTime , oldNote , paid ) ;
2017-06-06 17:40:14 +00:00
uint64 idx = hNote2ddx [ hNote ] ;
if ( idx > 0 ) return idx ;
idx = uint64 ( notes . length ) ;
2017-06-26 17:54:28 +00:00
hNote2ddx [ hNote ] = idx ;
2017-06-27 11:08:23 +00:00
notes . push ( Note ( 0 , owner , delegationChain , proposedProject , commitTime , oldNote , paid ) ) ;
2017-06-06 17:40:14 +00:00
return idx ;
}
function findManager ( uint64 idManager ) internal returns ( NoteManager storage ) {
2017-07-13 17:12:45 +00:00
require ( idManager < managers . length ) ;
2017-06-06 17:40:14 +00:00
return managers [ idManager ] ;
}
function findNote ( uint64 idNote ) internal returns ( Note storage ) {
2017-07-13 17:12:45 +00:00
require ( idNote < notes . length ) ;
2017-06-06 17:40:14 +00:00
return notes [ idNote ] ;
}
2017-07-09 17:04:02 +00:00
// a constant for the case that a delegate is requested that is not a delegate in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF ;
2017-07-13 17:21:53 +00:00
2017-07-09 17:04:02 +00:00
// helper function that searches the delegationChain fro a specific delegate and
2017-09-29 10:11:13 +00:00
// level of delegation returns their idx in the delegation chain which reflect their level of authority
2017-07-09 17:04:02 +00:00
function getDelegateIdx ( Note n , uint64 idDelegate ) internal returns ( uint64 ) {
for ( uint i = 0 ; i < n . delegationChain . length ; i ++ ) {
if ( n . delegationChain [ i ] == idDelegate ) return uint64 ( i ) ;
}
return NOTFOUND ;
}
2017-08-18 16:47:22 +00:00
// helper function that returns the note level solely to check that transfers
// between Projects not violate MAX_INTERPROJECT_LEVEL
function getNoteLevel ( Note n ) internal returns ( uint ) {
2017-07-09 17:04:02 +00:00
if ( n . oldNote == 0 ) return 0 ; //changed
2017-08-13 11:23:00 +00:00
Note storage oldN = findNote ( n . oldNote ) ;
2017-08-18 16:47:22 +00:00
return getNoteLevel ( oldN ) + 1 ;
2017-07-09 17:04:02 +00:00
}
2017-08-18 16:47:22 +00:00
2017-09-13 12:41:08 +00:00
// helper function that returns the max commit time of the owner and all the
// delegates
function maxCommitTime ( Note n ) internal returns ( uint commitTime ) {
NoteManager storage m = findManager ( n . owner ) ;
commitTime = m . commitTime ;
for ( uint i = 0 ; i < n . delegationChain . length ; i ++ ) {
m = findManager ( n . delegationChain [ i ] ) ;
if ( m . commitTime > commitTime ) commitTime = m . commitTime ;
}
}
2017-08-18 16:47:22 +00:00
// helper function that returns the project level solely to check that there
// are not too many Projects that violate MAX_SUBPROJECT_LEVEL
function getProjectLevel ( NoteManager m ) internal returns ( uint ) {
assert ( m . managerType == NoteManagerType . Project ) ;
if ( m . parentProject == 0 ) return ( 1 ) ;
NoteManager storage parentNM = findManager ( m . parentProject ) ;
return getProjectLevel ( parentNM ) ;
}
function isProjectCanceled ( uint64 projectId ) constant returns ( bool ) {
NoteManager storage m = findManager ( projectId ) ;
if ( m . managerType == NoteManagerType . Donor ) return false ;
assert ( m . managerType == NoteManagerType . Project ) ;
if ( m . canceled ) return true ;
if ( m . parentProject == 0 ) return false ;
return isProjectCanceled ( m . parentProject ) ;
}
function isProjectCanceled2 ( uint64 projectId ) constant returns ( bool ) {
NoteManager storage m = findManager ( projectId ) ;
return false ;
if ( m . managerType == NoteManagerType . Donor ) return false ;
assert ( m . managerType == NoteManagerType . Project ) ;
if ( m . canceled ) return true ;
if ( m . parentProject == 0 ) return false ;
return isProjectCanceled2 ( m . parentProject ) ;
}
2017-09-29 10:11:13 +00:00
// @notice A helper function for canceling projects
// @param idNote the note that may or may not be canceled
2017-07-09 17:04:02 +00:00
function getOldestNoteNotCanceled ( uint64 idNote ) internal constant returns ( uint64 ) { //todo rename
2017-06-27 11:08:23 +00:00
if ( idNote == 0 ) return 0 ;
2017-07-13 17:12:45 +00:00
Note storage n = findNote ( idNote ) ;
2017-08-18 16:47:22 +00:00
NoteManager storage manager = findManager ( n . owner ) ;
if ( manager . managerType == NoteManagerType . Donor ) return idNote ;
assert ( manager . managerType == NoteManagerType . Project ) ;
if ( ! isProjectCanceled ( n . owner ) ) return idNote ;
return getOldestNoteNotCanceled ( n . oldNote ) ;
2017-06-06 17:40:14 +00:00
}
2017-09-18 13:43:09 +00:00
function checkManagerOwner ( NoteManager m ) internal constant {
require ( ( msg . sender == m . addr ) || ( msg . sender == address ( m . plugin ) ) ) ;
}
2017-06-06 17:40:14 +00:00
}