Created
September 22, 2017 22:31
-
-
Save 3esmit/ad75fe693140e2916844473481868676 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.11; | |
import "../token/MiniMeToken.sol"; | |
/** | |
* @title DelegationProxy | |
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) | |
* Create a delegation proxy to MiniMeTokens that store checkpoints from all changes and parent proxy to fall back in case of no delegation at this level. | |
*/ | |
contract DelegationProxy { | |
DelegationProxy public parentProxy; | |
//mapping (address => Delegation[]) public delegations; | |
event Delegated(address who, address to); | |
mapping (address => Delegate[]) public delegates; | |
mapping (address => Delegator[]) public delegators; | |
/*struct Delegation { | |
uint128 fromBlock; //when this was updated | |
address to; //who recieved the delegaton | |
address[] from; //who is delegation | |
uint256 toIndex; //index in from array of `to` | |
}*/ | |
struct Delegate { | |
uint128 fromBlock; //when this was updated | |
address[] from; //who is delegation | |
} | |
struct Delegator { | |
uint128 fromBlock; //when this was updated | |
address to; //who recieved the delegaton | |
uint256 toIndex; //index in from array of `to` | |
} | |
function DelegationProxy(address _parentProxy) { | |
parentProxy = DelegationProxy(_parentProxy); | |
} | |
function delegatedToAt(address _who, uint _block) returns (address addr) { | |
Delegator[] storage checkpoints = delegators[_who]; | |
//In case there is no registry | |
if (checkpoints.length == 0) { | |
if (address(parentProxy) != 0x0) { | |
return parentProxy.delegatedToAt(_who, _block); | |
} else { | |
return; | |
} | |
} | |
Delegator memory d = _getMemoryAt(checkpoints, _block); | |
// Case user set delegation to parentProxy; | |
if (d.to == address(parentProxy)) { | |
return parentProxy.delegatedToAt(_who, _block); | |
} | |
return d.to; | |
} | |
function delegatedInfluenceFromAt(address _who, address _token, uint _block) constant returns(uint256 _total) { | |
Delegate[] storage checkpoints = delegates[_who]; | |
//In case there is no registry | |
if (checkpoints.length == 0) { | |
if (address(parentProxy) != 0x0) { | |
return parentProxy.delegatedInfluenceFromAt(_who, _token, _block); | |
} else { | |
return 0; | |
} | |
} | |
Delegate memory d = _getMemoryAt(checkpoints, _block); | |
// Case user set delegation to parentProxy; | |
//if (d.to == address(parentProxy)) { | |
// return parentProxy.delegatedInfluenceFromAt(_who, _token, _block); | |
//} | |
uint _len = d.from.length; | |
for (uint256 i = 0; _len > i; i++) { | |
address _from = d.from[i]; | |
_total = MiniMeToken(_token).balanceOfAt(_from, _block); // source of _who votes | |
_total += delegatedInfluenceFromAt(_from, _token, _block); //sum the from delegation votes | |
} | |
} | |
/** | |
* @notice Reads delegation at a point in history | |
* @param _who The address `_who` votes are being delegated to (full chain) | |
* @param _block From what block | |
* @return address delegated, 0x0 if not delegating | |
*/ | |
function delegationOfAt(address _who, uint _block) constant returns(address) { | |
address delegate = delegatedToAt(_who, _block); | |
if (delegate != 0x0) { //_who is delegating? | |
return delegationOfAt(delegate, _block); //load the delegation of _who delegation | |
} else { | |
return _who; //reached the endpoint of delegation | |
} | |
} | |
/** | |
* @notice Reads the sum of votes a address have at a point in history | |
* @param _who From what address | |
* @param _token address of MiniMeToken | |
* @param _block From what block | |
* @return amount of votes | |
*/ | |
function influenceOfAt(address _who, MiniMeToken _token, uint _block) constant returns(uint256 _total) { | |
if (delegationOfAt(_who, _block) == 0x0) { //is endpoint of delegation? | |
_total = MiniMeToken(_token).balanceOfAt(_who, _block); // source of _who votes | |
_total += delegatedInfluenceFromAt(_who, _token, _block); //calcule the votes delegated to _who | |
} else { | |
_total = 0; //no influence because were delegated | |
} | |
} | |
/** | |
* @notice changes the delegation of an account, if _to 0x00: delegate to self. | |
* In case of having a parent proxy, if never defined, fall back to parent proxy. | |
* If once defined and want to delegate to parent proxy, set `_to` as parent address. | |
* @param _to to what address the caller address will delegate to? | |
*/ | |
function delegate(address _to) { | |
require(delegationOfAt(_to, block.number) != msg.sender); //block impossible circular delegation | |
Delegated(msg.sender, _to); | |
Delegator memory _newFrom; | |
Delegator[] storage fromHistory = delegators[msg.sender]; | |
if (fromHistory.length > 0) { | |
_newFrom = fromHistory[fromHistory.length - 1]; | |
if (_newFrom.to != 0x0) { //was delegating? remove old link | |
_removeDelegated(_newFrom.to, _newFrom.toIndex); | |
} | |
} | |
//Add the new delegation | |
_newFrom.fromBlock = uint128(block.number); | |
_newFrom.to = _to;//register where our delegation is going | |
if (_to != 0x0) { //_to is an account? | |
_newFrom.toIndex = _addDelegated(msg.sender,_to); | |
} else { | |
_newFrom.toIndex = 0; //zero index | |
} | |
fromHistory.push(_newFrom);//reguster `from` delegation update; | |
} | |
/// @dev `_getDelegationAt` retrieves the delegation at a given block number | |
/// @param checkpoints The memory being queried | |
/// @param _block The block number to retrieve the value at | |
/// @return The delegation being queried | |
function _getMemoryAt(Delegate[] storage checkpoints, uint _block) constant internal returns (Delegate d) { | |
// Case last checkpoint is the one; | |
if (_block >= checkpoints[checkpoints.length-1].fromBlock) { | |
d = checkpoints[checkpoints.length-1]; | |
} else { | |
// Lookup in array; | |
uint min = 0; | |
uint max = checkpoints.length-1; | |
while (max > min) { | |
uint mid = (max + min + 1) / 2; | |
if (checkpoints[mid].fromBlock<=_block) { | |
min = mid; | |
} else { | |
max = mid-1; | |
} | |
} | |
d = checkpoints[min]; | |
} | |
} | |
/// @dev `_getDelegationAt` retrieves the delegation at a given block number | |
/// @param checkpoints The memory being queried | |
/// @param _block The block number to retrieve the value at | |
/// @return The delegation being queried | |
function _getMemoryAt(Delegator[] storage checkpoints, uint _block) constant internal returns (Delegator d) { | |
// Case last checkpoint is the one; | |
if (_block >= checkpoints[checkpoints.length-1].fromBlock) { | |
d = checkpoints[checkpoints.length-1]; | |
} else { | |
// Lookup in array; | |
uint min = 0; | |
uint max = checkpoints.length-1; | |
while (max > min) { | |
uint mid = (max + min + 1) / 2; | |
if (checkpoints[mid].fromBlock<=_block) { | |
min = mid; | |
} else { | |
max = mid-1; | |
} | |
} | |
d = checkpoints[min]; | |
} | |
} | |
/** | |
* @dev removes address from list. | |
*/ | |
function _removeDelegated(address _to, uint _toIndex) private { | |
Delegate[] storage oldTo = delegates[_to]; | |
uint _oldToLen = oldTo.length; | |
require(_oldToLen > 0); | |
Delegate memory _newOldTo = oldTo[_oldToLen - 1]; | |
_newOldTo.from[_toIndex] = _newOldTo.from[_newOldTo.from.length - 1]; | |
oldTo.push(_newOldTo); | |
oldTo[_oldToLen].from.length--; | |
} | |
/** | |
* @dev add address to listt. | |
*/ | |
function _addDelegated(address _from, address _to) private returns (uint _toIndex) { | |
Delegate memory _newTo; | |
Delegate[] storage toHistory = delegates[_to]; | |
uint toHistLen = toHistory.length; | |
if (toHistLen > 0) { | |
_newTo = toHistory[toHistLen - 1]; | |
} | |
_newTo.fromBlock = uint128(block.number); | |
_toIndex = _newTo.from.length; //link to index | |
toHistory.push(_newTo); //register `to` delegated from | |
toHistory[toHistLen].from.push(_from); //add the delegated from in to list | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment