pragma solidity ^0.4.18; import "./ILiquidPledgingPlugin.sol"; import "./EternallyPersistentLib.sol"; //18,446,744,070,000,000,000 uint64 //1,516,144,546,228,000 uint56 library PledgeAdmins { using EternallyPersistentLib for EternalStorage; //TODO we can pack some of these struct values, which should save space. TEST THIS //TODO making functions public may lower deployment cost, but increase gas / tx costs. TEST THIS //TODO is it cheaper to issue a storage check before updating? where should this be done? EternalStorage? string constant class = "PledgeAdmins"; bytes32 constant admins = keccak256("pledgeAdmins"); enum PledgeAdminType { Giver, Delegate, Project } /// @notice Creates a Giver Admin with the `msg.sender` as the Admin address /// @param name The name used to identify the Giver /// @param url The link to the Giver's profile often an IPFS hash /// @param commitTime The length of time in seconds the Giver has to /// veto when the Giver's delegates Pledge funds to a project /// @param plugin This is Giver's liquid pledge plugin allowing for /// extended functionality /// @return idGiver The id number used to reference this Admin function addGiver( EternalStorage _storage, string name, string url, uint commitTime, ILiquidPledgingPlugin plugin ) internal returns (uint idGiver) { idGiver = _storage.stgCollectionAddItem(admins); // Save the fields // don't set adminType to save gas, b/c 0 is Giver _storage.stgObjectSetAddress(class, idGiver, "addr", msg.sender); _storage.stgObjectSetString(class, idGiver, "name", name); _storage.stgObjectSetString(class, idGiver, "url", url); _storage.stgObjectSetUInt(class, idGiver, "commitTime", commitTime); _storage.stgObjectSetAddress(class, idGiver, "plugin", address(plugin)); GiverAdded(idGiver); } event GiverAdded(uint indexed idGiver); /// @notice Updates a Giver's info to change the address, name, url, or /// commitTime, it cannot be used to change a plugin, and it must be called /// by the current address of the Giver /// @param idGiver This is the Admin id number used to specify the Giver /// @param newAddr The new address that represents this Giver /// @param newName The new name used to identify the Giver /// @param newUrl The new link to the Giver's profile often an IPFS hash /// @param newCommitTime Sets the length of time in seconds the Giver has to /// veto when the Giver's delegates Pledge funds to a project function updateGiver( EternalStorage _storage, uint idGiver, address newAddr, string newName, string newUrl, uint64 newCommitTime ) public { require(getAdminType(_storage, idGiver) == PledgeAdminType.Giver); // Must be a Giver require(getAdminAddr(_storage, idGiver) == msg.sender); // Current addr had to send this tx // Save the fields _storage.stgObjectSetAddress(class, idGiver, "addr", newAddr); _storage.stgObjectSetString(class, idGiver, "name", newName); _storage.stgObjectSetString(class, idGiver, "url", newUrl); _storage.stgObjectSetUInt(class, idGiver, "commitTime", newCommitTime); GiverUpdated(idGiver); } event GiverUpdated(uint indexed idGiver); /// @notice Creates a Delegate Admin with the `msg.sender` as the Admin addr /// @param name The name used to identify the Delegate /// @param url The link to the Delegate's profile often an IPFS hash /// @param commitTime Sets the length of time in seconds that this delegate /// can be vetoed. Whenever this delegate is in a delegate chain the time /// allowed to veto any event must be greater than or equal to this time. /// @param plugin This is Delegate's liquid pledge plugin allowing for /// extended functionality /// @return idxDelegate The id number used to reference this Delegate within /// the admins array function addDelegate( EternalStorage _storage, string name, string url, uint64 commitTime, ILiquidPledgingPlugin plugin ) internal returns (uint idDelegate) { idDelegate = _storage.stgCollectionAddItem(admins);//, idDelegate); // Save the fields _storage.stgObjectSetUInt(class, idDelegate, "adminType", uint(PledgeAdminType.Delegate)); _storage.stgObjectSetAddress(class, idDelegate, "addr", msg.sender); _storage.stgObjectSetString(class, idDelegate, "name", name); _storage.stgObjectSetString(class, idDelegate, "url", url); _storage.stgObjectSetUInt(class, idDelegate, "commitTime", commitTime); _storage.stgObjectSetAddress(class, idDelegate, "plugin", address(plugin)); DelegateAdded(idDelegate); } event DelegateAdded(uint indexed idDelegate); /// @notice Updates a Delegate's info to change the address, name, url, or /// commitTime, it cannot be used to change a plugin, and it must be called /// by the current address of the Delegate /// @param idDelegate The Admin id number used to specify the Delegate /// @param newAddr The new address that represents this Delegate /// @param newName The new name used to identify the Delegate /// @param newUrl The new link to the Delegate's profile often an IPFS hash /// @param newCommitTime Sets the length of time in seconds that this /// delegate can be vetoed. Whenever this delegate is in a delegate chain /// the time allowed to veto any event must be greater than or equal to /// this time. function updateDelegate( EternalStorage _storage, uint idDelegate, address newAddr, string newName, string newUrl, uint64 newCommitTime ) public { require(getAdminType(_storage, idDelegate) == PledgeAdminType.Delegate); require(getAdminAddr(_storage, idDelegate) == msg.sender); // Current addr had to send this tx // Save the fields _storage.stgObjectSetAddress(class, idDelegate, "addr", newAddr); _storage.stgObjectSetString(class, idDelegate, "name", newName); _storage.stgObjectSetString(class, idDelegate, "url", newUrl); _storage.stgObjectSetUInt(class, idDelegate, "commitTime", newCommitTime); DelegateUpdated(idDelegate); } event DelegateUpdated(uint indexed idDelegate); /// @notice Creates a Project Admin with the `msg.sender` as the Admin addr /// @param name The name used to identify the Project /// @param url The link to the Project's profile often an IPFS hash /// @param projectAdmin The address for the trusted project manager /// @param parentProject The Admin id number for the parent project or 0 if /// there is no parentProject /// @param commitTime Sets the length of time in seconds the Project has to /// veto when the Project delegates to another Delegate and they pledge /// those funds to a project /// @param plugin This is Project's liquid pledge plugin allowing for /// extended functionality /// @return idProject The id number used to reference this Admin function addProject( EternalStorage _storage, string name, string url, address projectAdmin, uint64 parentProject, uint64 commitTime, ILiquidPledgingPlugin plugin ) internal returns (uint idProject) { idProject = _storage.stgCollectionAddItem(admins);//, idProject); // Save the fields _storage.stgObjectSetUInt(class, idProject, "adminType", uint(PledgeAdminType.Project)); _storage.stgObjectSetAddress(class, idProject, "addr", projectAdmin); _storage.stgObjectSetString(class, idProject, "name", name); _storage.stgObjectSetString(class, idProject, "url", url); // NOTICE: we do not verify that the parentProject has a `Project` adminType // this is expected to be done by the calling method _storage.stgObjectSetUInt(class, idProject, "parentProject", parentProject); _storage.stgObjectSetUInt(class, idProject, "commitTime", commitTime); _storage.stgObjectSetAddress(class, idProject, "plugin", address(plugin)); ProjectAdded(idProject); } event ProjectAdded(uint indexed idProject); /// @notice Updates a Project's info to change the address, name, url, or /// commitTime, it cannot be used to change a plugin or a parentProject, /// and it must be called by the current address of the Project /// @param idProject The Admin id number used to specify the Project /// @param newAddr The new address that represents this Project /// @param newName The new name used to identify the Project /// @param newUrl The new link to the Project's profile often an IPFS hash /// @param newCommitTime Sets the length of time in seconds the Project has /// to veto when the Project delegates to a Delegate and they pledge those /// funds to a project function updateProject( EternalStorage _storage, uint idProject, address newAddr, string newName, string newUrl, uint64 newCommitTime ) public { require(getAdminType(_storage, idProject) == PledgeAdminType.Project); require(getAdminAddr(_storage, idProject) == msg.sender); // Current addr had to send this tx // Save the fields _storage.stgObjectSetAddress(class, idProject, "addr", newAddr); _storage.stgObjectSetString(class, idProject, "name", newName); _storage.stgObjectSetString(class, idProject, "url", newUrl); _storage.stgObjectSetUInt(class, idProject, "commitTime", newCommitTime); ProjectUpdated(idProject); } event ProjectUpdated(uint indexed idAdmin); function cancelProject(EternalStorage _storage, uint idProject) internal { _storage.stgObjectSetBoolean(class, idProject, "canceled", true); CancelProject(idProject); } /// @notice A getter to find if a specified Project has been canceled /// @param projectId The Admin id number used to specify the Project /// @return True if the Project has been canceled function isProjectCanceled(EternalStorage _storage, uint projectId) internal constant returns (bool) { require(pledgeAdminsCount(_storage) >= projectId); PledgeAdminType adminType = getAdminType(_storage, projectId); if (adminType == PledgeAdminType.Giver) return false; assert(adminType == PledgeAdminType.Project); if (getAdminCanceled(_storage, projectId)) return true; uint parentProject = getAdminParentProject(_storage, projectId); if (parentProject == 0) return false; return isProjectCanceled(_storage, parentProject); } event CancelProject(uint indexed idProject); /// @notice A constant getter used to check how many total Admins exist /// @return The total number of admins (Givers, Delegates and Projects) . function pledgeAdminsCount(EternalStorage _storage) internal constant returns(uint) { return _storage.stgCollectionLength(admins); } /// @notice A constant getter to check the details of a specified Admin /// @return addr Account or contract address for admin /// @return name Name of the pledgeAdmin /// @return url The link to the Project's profile often an IPFS hash /// @return commitTime The length of time in seconds the Admin has to veto /// when the Admin delegates to a Delegate and that Delegate pledges those /// funds to a project /// @return parentProject The Admin id number for the parent project or 0 /// if there is no parentProject /// @return canceled 0 for Delegates & Givers, true if a Project has been /// canceled /// @return plugin This is Project's liquidPledging plugin allowing for /// extended functionality function getAdmin(EternalStorage _storage, uint idAdmin) internal view returns ( PledgeAdminType adminType, address addr, string name, string url, uint64 commitTime, uint64 parentProject, bool canceled, address plugin ) { adminType = getAdminType(_storage, idAdmin); addr = getAdminAddr(_storage, idAdmin); name = getAdminName(_storage, idAdmin); url = _storage.stgObjectGetString(class, idAdmin, "url"); commitTime = uint64(getAdminCommitTime(_storage, idAdmin)); // parentProject & canceled only belong to Project admins, // so don't waste the gas to fetch the data if (adminType == PledgeAdminType.Project) { parentProject = uint64(getAdminParentProject(_storage, idAdmin)); canceled = getAdminCanceled(_storage, idAdmin); } plugin = getAdminPlugin(_storage, idAdmin); } /// @notice Find the level of authority a specific Project has /// using a recursive loop /// @param idProject The id of the Project being queried /// @return The level of authority a specific Project has function getProjectLevel(EternalStorage _storage, uint idProject) internal returns(uint) { assert(getAdminType(_storage, idProject) == PledgeAdminType.Project); uint parentProject = getAdminParentProject(_storage, idProject); if (parentProject == 0) return(1); return getProjectLevel(_storage, parentProject) + 1; } //////// // Methods to fetch individual attributes of a PledgeAdmin /////// // costs ~10k gas function getAdminType( EternalStorage _storage, uint idAdmin ) internal view returns (PledgeAdminType) { return PledgeAdminType(_storage.stgObjectGetUInt(class, idAdmin, "adminType")); } // costs ~10k gas function getAdminAddr( EternalStorage _storage, uint idAdmin ) internal view returns (address) { return _storage.stgObjectGetAddress(class, idAdmin, "addr"); } // costs ~8k gas function getAdminName( EternalStorage _storage, uint idAdmin ) internal view returns (string) { return _storage.stgObjectGetString(class, idAdmin, "name"); } // costs ~10k gas function getAdminParentProject( EternalStorage _storage, uint idAdmin ) internal view returns (uint) { return _storage.stgObjectGetUInt(class, idAdmin, "parentProject"); } // costs ~10k gas function getAdminCanceled( EternalStorage _storage, uint idAdmin ) internal view returns (bool) { return _storage.stgObjectGetBoolean(class, idAdmin, "canceled"); } // costs ~10k gas function getAdminPlugin( EternalStorage _storage, uint idAdmin ) internal view returns (address) { return _storage.stgObjectGetAddress(class, idAdmin, "plugin"); } // costs ~10k gas function getAdminCommitTime( EternalStorage _storage, uint idAdmin ) internal view returns (uint) { return _storage.stgObjectGetUInt(class, idAdmin, "commitTime"); } }