2017-08-26 19:46:34 +00:00
2017-09-13 12:41:08 +00:00
//File: contracts/ILiquidPledgingPlugin.sol
pragma solidity ^ 0 . 4 . 11 ;
contract ILiquidPledgingPlugin {
2017-10-03 11:37:45 +00:00
/// @notice Plugins are used (much like web hooks) to initiate an action
2017-10-03 10:08:47 +00:00
/// upon any donation, delegation, or transfer; this is an optional feature
/// and allows for extreme customization of the contract
2017-10-03 11:37:45 +00:00
/// @param context The situation that is triggering the plugin:
/// 0 -> Plugin for the owner transferring pledge to another party
2017-10-03 10:08:47 +00:00
/// 1 -> Plugin for the first delegate transferring pledge to another party
/// 2 -> Plugin for the second delegate transferring pledge to another party
2017-09-13 12:41:08 +00:00
/// ...
2017-10-04 23:27:23 +00:00
/// 255 -> Plugin for the intendedProject transferring pledge to another party
2017-09-13 12:41:08 +00:00
///
2017-10-03 10:08:47 +00:00
/// 256 -> Plugin for the owner receiving pledge to another party
/// 257 -> Plugin for the first delegate receiving pledge to another party
/// 258 -> Plugin for the second delegate receiving pledge to another party
2017-09-13 12:41:08 +00:00
/// ...
2017-10-04 23:27:23 +00:00
/// 511 -> Plugin for the intendedProject receiving pledge to another party
2017-10-03 10:08:47 +00:00
function beforeTransfer (
2017-10-04 10:55:46 +00:00
uint64 pledgeManager ,
uint64 pledgeFrom ,
uint64 pledgeTo ,
2017-10-03 10:08:47 +00:00
uint64 context ,
uint amount
) returns ( uint maxAllowed ) ;
function afterTransfer (
2017-10-04 10:55:46 +00:00
uint64 pledgeManager ,
uint64 pledgeFrom ,
uint64 pledgeTo ,
2017-10-03 10:08:47 +00:00
uint64 context ,
uint amount ) ;
2017-09-13 12:41:08 +00:00
}
2017-08-26 19:46:34 +00:00
//File: ./contracts/LiquidPledgingBase.sol
pragma solidity ^ 0 . 4 . 11 ;
2017-09-13 12:41:08 +00:00
2017-10-03 10:08:47 +00:00
/// @dev This is declares a few functions from `Vault` so that the
/// `LiquidPledgingBase` contract can interface with the `Vault` contract
2017-08-26 19:46:34 +00:00
contract Vault {
function authorizePayment ( bytes32 _ref , address _dest , uint _amount ) ;
function ( ) payable ;
}
contract LiquidPledgingBase {
2017-10-03 10:20:23 +00:00
// Limits inserted to prevent large loops that could prevent canceling
2017-08-26 19:46:34 +00:00
uint constant MAX_DELEGATES = 20 ;
2017-10-04 23:27:23 +00:00
uint constant MAX_SUBPROJECT_LEVEL = 20 ;
uint constant MAX_INTERPROJECT_LEVEL = 20 ;
2017-08-26 19:46:34 +00:00
2017-10-04 23:27:23 +00:00
enum PledgeAdminType { Giver , Delegate , Project }
2017-10-04 08:29:41 +00:00
enum PaymentState { Pledged , Paying , Paid } // TODO name change Pledged
2017-10-03 10:08:47 +00:00
2017-10-04 08:24:35 +00:00
/// @dev This struct defines the details of each the PledgeAdmin, these
/// PledgeAdmins can own pledges and act as delegates
struct PledgeAdmin { // TODO name change PledgeAdmin
2017-10-04 23:27:23 +00:00
PledgeAdminType adminType ; // Giver, Delegate or Project
2017-10-03 10:08:47 +00:00
address addr ; // account or contract address for admin
2017-10-03 10:20:23 +00:00
string name ;
2017-10-04 09:40:26 +00:00
string url ;
2017-10-03 10:08:47 +00:00
uint64 commitTime ; // In seconds, used for Givers' & Delegates' vetos
2017-10-04 23:27:23 +00:00
uint64 parentProject ; // Only for projects
bool canceled ; //Always false except for canceled projects
2017-10-03 10:08:47 +00:00
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-08-26 19:46:34 +00:00
}
2017-10-03 12:42:21 +00:00
struct Pledge {
2017-08-26 19:46:34 +00:00
uint amount ;
2017-10-04 08:24:35 +00:00
uint64 owner ; // PledgeAdmin
2017-10-03 10:08:47 +00:00
uint64 [ ] delegationChain ; // list of index numbers
2017-10-04 23:27:23 +00:00
uint64 intendedProject ; // TODO change the name only used for when delegates are precommiting to a project
uint64 commitTime ; // When the intendedProject will become the owner
2017-10-03 12:42:21 +00:00
uint64 oldPledge ; // this points to the Pledge[] index that the Pledge was derived from
2017-08-26 19:46:34 +00:00
PaymentState paymentState ;
}
2017-10-03 12:42:21 +00:00
Pledge [ ] pledges ;
2017-10-04 08:24:35 +00:00
PledgeAdmin [ ] admins ; //The list of pledgeAdmins 0 means there is no admin
2017-08-26 19:46:34 +00:00
Vault public vault ;
2017-10-03 12:42:21 +00:00
// this mapping allows you to search for a specific pledge's index number by the hash of that pledge
mapping ( bytes32 => uint64 ) hPledge2idx ; //TODO Fix typo
2017-08-26 19:46:34 +00:00
/////
// Modifiers
/////
modifier onlyVault ( ) {
require ( msg . sender == address ( vault ) ) ;
_ ;
}
//////
// Constructor
//////
2017-10-03 10:08:47 +00:00
/// @notice The Constructor creates the `LiquidPledgingBase` on the blockchain
/// @param _vault Where the ETH is stored that the pledges represent
2017-08-26 19:46:34 +00:00
function LiquidPledgingBase ( address _vault ) {
2017-10-04 08:24:35 +00:00
admins . length = 1 ; // we reserve the 0 admin
2017-10-03 12:42:21 +00:00
pledges . length = 1 ; // we reserve the 0 pledge
2017-08-26 19:46:34 +00:00
vault = Vault ( _vault ) ;
}
///////
2017-10-04 08:24:35 +00:00
// Adminss functions
2017-08-26 19:46:34 +00:00
//////
2017-10-03 10:20:23 +00:00
/// @notice Creates a giver.
2017-10-04 09:40:26 +00:00
function addGiver ( string name , string url , uint64 commitTime , ILiquidPledgingPlugin plugin
2017-10-03 10:20:23 +00:00
) returns ( uint64 idGiver ) {
2017-09-14 18:06:58 +00:00
2017-10-04 08:24:35 +00:00
idGiver = uint64 ( admins . length ) ;
2017-09-14 18:06:58 +00:00
2017-10-04 08:24:35 +00:00
admins . push ( PledgeAdmin (
PledgeAdminType . Giver ,
2017-08-26 19:46:34 +00:00
msg . sender ,
name ,
2017-10-04 09:40:26 +00:00
url ,
2017-08-26 19:46:34 +00:00
commitTime ,
0 ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-08-26 19:46:34 +00:00
2017-10-03 10:20:23 +00:00
GiverAdded ( idGiver ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-03 10:20:23 +00:00
event GiverAdded ( uint64 indexed idGiver ) ;
2017-08-26 19:46:34 +00:00
2017-10-03 10:20:23 +00:00
///@notice Changes the address, name or commitTime associated with a specific giver
function updateGiver (
uint64 idGiver ,
2017-08-26 19:46:34 +00:00
address newAddr ,
string newName ,
2017-10-04 09:40:26 +00:00
string newUrl ,
2017-09-18 13:43:09 +00:00
uint64 newCommitTime )
2017-08-26 19:46:34 +00:00
{
2017-10-04 08:24:35 +00:00
PledgeAdmin storage giver = findAdmin ( idGiver ) ;
require ( giver . adminType == PledgeAdminType . Giver ) ; //Must be a Giver
2017-10-03 12:42:21 +00:00
require ( giver . addr == msg . sender ) ; //current addr had to originate this tx
2017-10-03 10:20:23 +00:00
giver . addr = newAddr ;
giver . name = newName ;
2017-10-04 09:40:26 +00:00
giver . url = newUrl ;
2017-10-03 10:20:23 +00:00
giver . commitTime = newCommitTime ;
GiverUpdated ( idGiver ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-03 10:20:23 +00:00
event GiverUpdated ( uint64 indexed idGiver ) ;
2017-08-26 19:46:34 +00:00
2017-10-03 10:08:47 +00:00
/// @notice Creates a new Delegate
2017-10-04 09:40:26 +00:00
function addDelegate ( string name , string url , uint64 commitTime , ILiquidPledgingPlugin plugin ) returns ( uint64 idDelegate ) { //TODO return index number
2017-09-14 18:06:58 +00:00
2017-10-04 08:24:35 +00:00
idDelegate = uint64 ( admins . length ) ;
2017-09-14 18:06:58 +00:00
2017-10-04 08:24:35 +00:00
admins . push ( PledgeAdmin (
PledgeAdminType . Delegate ,
2017-08-26 19:46:34 +00:00
msg . sender ,
name ,
2017-10-04 09:40:26 +00:00
url ,
2017-09-13 12:41:08 +00:00
commitTime ,
2017-08-26 19:46:34 +00:00
0 ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-08-26 19:46:34 +00:00
2017-10-03 10:08:47 +00:00
DelegateAdded ( idDelegate ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-03 10:08:47 +00:00
event DelegateAdded ( uint64 indexed idDelegate ) ;
2017-08-26 19:46:34 +00:00
2017-10-03 10:08:47 +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-10-04 09:40:26 +00:00
string newUrl ,
2017-09-18 13:43:09 +00:00
uint64 newCommitTime ) {
2017-10-04 08:24:35 +00:00
PledgeAdmin storage delegate = findAdmin ( idDelegate ) ;
require ( delegate . adminType == PledgeAdminType . Delegate ) ;
2017-08-26 19:46:34 +00:00
require ( delegate . addr == msg . sender ) ;
delegate . addr = newAddr ;
delegate . name = newName ;
2017-10-04 09:40:26 +00:00
delegate . url = newUrl ;
2017-09-13 12:41:08 +00:00
delegate . commitTime = newCommitTime ;
2017-08-26 19:46:34 +00:00
DelegateUpdated ( idDelegate ) ;
}
event DelegateUpdated ( uint64 indexed idDelegate ) ;
2017-10-04 23:27:23 +00:00
/// @notice Creates a new Project
function addProject ( string name , string url , address projectAdmin , uint64 parentProject , uint64 commitTime , ILiquidPledgingPlugin plugin ) returns ( uint64 idProject ) {
if ( parentProject != 0 ) {
PledgeAdmin storage pa = findAdmin ( parentProject ) ;
require ( pa . adminType == PledgeAdminType . Project ) ;
require ( getProjectLevel ( pa ) < MAX_SUBPROJECT_LEVEL ) ;
2017-08-26 19:46:34 +00:00
}
2017-09-14 18:06:58 +00:00
2017-10-04 23:27:23 +00:00
idProject = uint64 ( admins . length ) ;
2017-09-14 18:06:58 +00:00
2017-10-04 08:24:35 +00:00
admins . push ( PledgeAdmin (
2017-10-04 23:27:23 +00:00
PledgeAdminType . Project ,
projectAdmin ,
2017-08-26 19:46:34 +00:00
name ,
2017-10-04 09:40:26 +00:00
url ,
2017-08-26 19:46:34 +00:00
commitTime ,
2017-10-04 23:27:23 +00:00
parentProject ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-08-26 19:46:34 +00:00
2017-09-14 18:06:58 +00:00
2017-10-04 23:27:23 +00:00
ProjectAdded ( idProject ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-04 23:27:23 +00:00
event ProjectAdded ( uint64 indexed idProject ) ;
2017-08-26 19:46:34 +00:00
2017-10-04 23:27:23 +00:00
///@notice Changes the address, name or commitTime associated with a specific Project
function updateProject (
uint64 idProject ,
2017-09-18 13:43:09 +00:00
address newAddr ,
string newName ,
2017-10-04 09:40:26 +00:00
string newUrl ,
2017-09-18 13:43:09 +00:00
uint64 newCommitTime )
{
2017-10-04 23:27:23 +00:00
PledgeAdmin storage project = findAdmin ( idProject ) ;
require ( project . adminType == PledgeAdminType . Project ) ;
require ( project . addr == msg . sender ) ;
project . addr = newAddr ;
project . name = newName ;
project . url = newUrl ;
project . commitTime = newCommitTime ;
ProjectUpdated ( idProject ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-04 23:27:23 +00:00
event ProjectUpdated ( uint64 indexed idAdmin ) ;
2017-08-26 19:46:34 +00:00
//////////
// Public constant functions
//////////
2017-10-03 12:42:21 +00:00
/// @notice Public constant that states how many pledgess are in the system
function numberOfPledges ( ) constant returns ( uint ) {
return pledges . length - 1 ;
2017-08-26 19:46:34 +00:00
}
2017-10-03 12:42:21 +00:00
/// @notice Public constant that states the details of the specified Pledge
function getPledge ( uint64 idPledge ) constant returns (
2017-08-26 19:46:34 +00:00
uint amount ,
uint64 owner ,
uint64 nDelegates ,
2017-10-04 23:27:23 +00:00
uint64 intendedProject ,
2017-08-26 19:46:34 +00:00
uint64 commitTime ,
2017-10-03 12:42:21 +00:00
uint64 oldPledge ,
2017-08-26 19:46:34 +00:00
PaymentState paymentState
) {
2017-10-03 12:42:21 +00:00
Pledge storage n = findPledge ( idPledge ) ;
2017-08-26 19:46:34 +00:00
amount = n . amount ;
owner = n . owner ;
nDelegates = uint64 ( n . delegationChain . length ) ;
2017-10-04 23:27:23 +00:00
intendedProject = n . intendedProject ;
2017-08-26 19:46:34 +00:00
commitTime = n . commitTime ;
2017-10-03 12:42:21 +00:00
oldPledge = n . oldPledge ;
2017-08-26 19:46:34 +00:00
paymentState = n . paymentState ;
}
2017-10-03 10:08:47 +00:00
/// @notice Public constant that states the delegates one by one, because
/// an array cannot be returned
2017-10-03 12:42:21 +00:00
function getPledgeDelegate ( uint64 idPledge , uint idxDelegate ) constant returns (
2017-08-26 19:46:34 +00:00
uint64 idDelegate ,
address addr ,
string name
) {
2017-10-03 12:42:21 +00:00
Pledge storage n = findPledge ( idPledge ) ;
2017-08-26 19:46:34 +00:00
idDelegate = n . delegationChain [ idxDelegate - 1 ] ;
2017-10-04 08:24:35 +00:00
PledgeAdmin storage delegate = findAdmin ( idDelegate ) ;
2017-08-26 19:46:34 +00:00
addr = delegate . addr ;
name = delegate . name ;
}
2017-10-03 10:08:47 +00:00
/// @notice Public constant that states the number of admins in the system
2017-10-04 08:24:35 +00:00
function numberOfPledgeAdmins ( ) constant returns ( uint ) {
return admins . length - 1 ;
2017-08-26 19:46:34 +00:00
}
2017-10-03 10:20:23 +00:00
/// @notice Public constant that states the details of the specified admin
2017-10-04 08:24:35 +00:00
function getPledgeAdmin ( uint64 idAdmin ) constant returns (
PledgeAdminType adminType ,
2017-08-26 19:46:34 +00:00
address addr ,
string name ,
2017-10-04 09:40:26 +00:00
string url ,
2017-08-26 19:46:34 +00:00
uint64 commitTime ,
2017-10-04 23:27:23 +00:00
uint64 parentProject ,
2017-10-03 10:08:47 +00:00
bool canceled ,
address plugin )
2017-08-26 19:46:34 +00:00
{
2017-10-04 08:24:35 +00:00
PledgeAdmin storage m = findAdmin ( idAdmin ) ;
adminType = m . adminType ;
2017-08-26 19:46:34 +00:00
addr = m . addr ;
name = m . name ;
2017-10-04 09:40:26 +00:00
url = m . url ;
2017-08-26 19:46:34 +00:00
commitTime = m . commitTime ;
2017-10-04 23:27:23 +00:00
parentProject = m . parentProject ;
2017-08-26 19:46:34 +00:00
canceled = m . canceled ;
2017-10-03 10:08:47 +00:00
plugin = address ( m . plugin ) ;
2017-08-26 19:46:34 +00:00
}
////////
// Private methods
///////
2017-10-03 12:42:21 +00:00
/// @notice All pledges technically exist... but if the pledge hasn't been
2017-10-03 10:08:47 +00:00
/// created in this system yet then it wouldn't be in the hash array
2017-10-03 12:42:21 +00:00
/// hPledge2idx[]; this creates a Pledge with and amount of 0 if one is not
2017-10-03 10:20:23 +00:00
/// created already...
2017-10-04 10:55:46 +00:00
function findOrCreatePledge (
2017-08-26 19:46:34 +00:00
uint64 owner ,
uint64 [ ] delegationChain ,
2017-10-04 23:27:23 +00:00
uint64 intendedProject ,
2017-08-26 19:46:34 +00:00
uint64 commitTime ,
2017-10-03 12:42:21 +00:00
uint64 oldPledge ,
2017-08-26 19:46:34 +00:00
PaymentState paid
) internal returns ( uint64 )
{
2017-10-04 23:27:23 +00:00
bytes32 hPledge = sha3 ( owner , delegationChain , intendedProject , commitTime , oldPledge , paid ) ;
2017-10-03 12:42:21 +00:00
uint64 idx = hPledge2idx [ hPledge ] ;
2017-08-26 19:46:34 +00:00
if ( idx > 0 ) return idx ;
2017-10-03 12:42:21 +00:00
idx = uint64 ( pledges . length ) ;
hPledge2idx [ hPledge ] = idx ;
2017-10-04 23:27:23 +00:00
pledges . push ( Pledge ( 0 , owner , delegationChain , intendedProject , commitTime , oldPledge , paid ) ) ;
2017-08-26 19:46:34 +00:00
return idx ;
}
2017-10-04 08:24:35 +00:00
function findAdmin ( uint64 idAdmin ) internal returns ( PledgeAdmin storage ) {
require ( idAdmin < admins . length ) ;
return admins [ idAdmin ] ;
2017-08-26 19:46:34 +00:00
}
2017-10-03 12:42:21 +00:00
function findPledge ( uint64 idPledge ) internal returns ( Pledge storage ) {
require ( idPledge < pledges . length ) ;
return pledges [ idPledge ] ;
2017-08-26 19:46:34 +00:00
}
// a constant for the case that a delegate is requested that is not a delegate in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF ;
// helper function that searches the delegationChain fro a specific delegate and
2017-10-03 10:08:47 +00:00
// level of delegation returns their idx in the delegation chain which reflect their level of authority
2017-10-03 12:42:21 +00:00
function getDelegateIdx ( Pledge n , uint64 idDelegate ) internal returns ( uint64 ) {
2017-08-26 19:46:34 +00:00
for ( uint i = 0 ; i < n . delegationChain . length ; i ++ ) {
if ( n . delegationChain [ i ] == idDelegate ) return uint64 ( i ) ;
}
return NOTFOUND ;
}
2017-10-03 12:42:21 +00:00
// helper function that returns the pledge level solely to check that transfers
2017-10-04 23:27:23 +00:00
// between Projects not violate MAX_INTERPROJECT_LEVEL
2017-10-03 12:42:21 +00:00
function getPledgeLevel ( Pledge n ) internal returns ( uint ) {
if ( n . oldPledge == 0 ) return 0 ; //changed
Pledge storage oldN = findPledge ( n . oldPledge ) ;
return getPledgeLevel ( oldN ) + 1 ;
2017-08-26 19:46:34 +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
2017-10-03 12:42:21 +00:00
function maxCommitTime ( Pledge n ) internal returns ( uint commitTime ) {
2017-10-04 08:24:35 +00:00
PledgeAdmin storage m = findAdmin ( n . owner ) ;
2017-09-13 12:41:08 +00:00
commitTime = m . commitTime ;
for ( uint i = 0 ; i < n . delegationChain . length ; i ++ ) {
2017-10-04 08:24:35 +00:00
m = findAdmin ( n . delegationChain [ i ] ) ;
2017-09-13 12:41:08 +00:00
if ( m . commitTime > commitTime ) commitTime = m . commitTime ;
}
}
2017-08-26 19:46:34 +00:00
2017-10-04 23:27:23 +00:00
// helper function that returns the project level solely to check that there
// are not too many Projects that violate MAX_SUBCAMPAIGNS_LEVEL
function getProjectLevel ( PledgeAdmin m ) internal returns ( uint ) {
assert ( m . adminType == PledgeAdminType . Project ) ;
if ( m . parentProject == 0 ) return ( 1 ) ;
PledgeAdmin storage parentNM = findAdmin ( m . parentProject ) ;
return getProjectLevel ( parentNM ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-04 23:27:23 +00:00
function isProjectCanceled ( uint64 projectId ) constant returns ( bool ) {
PledgeAdmin storage m = findAdmin ( projectId ) ;
2017-10-04 08:24:35 +00:00
if ( m . adminType == PledgeAdminType . Giver ) return false ;
2017-10-04 23:27:23 +00:00
assert ( m . adminType == PledgeAdminType . Project ) ;
2017-08-26 19:46:34 +00:00
if ( m . canceled ) return true ;
2017-10-04 23:27:23 +00:00
if ( m . parentProject == 0 ) return false ;
return isProjectCanceled ( m . parentProject ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-04 23:27:23 +00:00
// @notice A helper function for canceling projects
2017-10-03 12:42:21 +00:00
// @param idPledge the pledge that may or may not be canceled
function getOldestPledgeNotCanceled ( uint64 idPledge ) internal constant returns ( uint64 ) { //todo rename
if ( idPledge == 0 ) return 0 ;
Pledge storage n = findPledge ( idPledge ) ;
2017-10-04 08:24:35 +00:00
PledgeAdmin storage admin = findAdmin ( n . owner ) ;
if ( admin . adminType == PledgeAdminType . Giver ) return idPledge ;
2017-08-26 19:46:34 +00:00
2017-10-04 23:27:23 +00:00
assert ( admin . adminType == PledgeAdminType . Project ) ;
2017-08-26 19:46:34 +00:00
2017-10-04 23:27:23 +00:00
if ( ! isProjectCanceled ( n . owner ) ) return idPledge ;
2017-08-26 19:46:34 +00:00
2017-10-03 12:42:21 +00:00
return getOldestPledgeNotCanceled ( n . oldPledge ) ;
2017-08-26 19:46:34 +00:00
}
2017-10-04 08:24:35 +00:00
function checkAdminOwner ( PledgeAdmin m ) internal constant {
2017-09-18 13:43:09 +00:00
require ( ( msg . sender == m . addr ) || ( msg . sender == address ( m . plugin ) ) ) ;
}
2017-08-26 19:46:34 +00:00
}