Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[eth-contracts] Delegation v0 + Governance v1 #340

Merged
merged 43 commits into from
Apr 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ee2a48d
Basic multiplier
hareeshnagaraj Mar 16, 2020
bfdce01
DIRTY STATE - fix unstake amount
hareeshnagaraj Mar 17, 2020
b98b554
more progress
hareeshnagaraj Mar 17, 2020
530e369
Test cleanup
hareeshnagaraj Mar 17, 2020
bc32af3
more cleanup
hareeshnagaraj Mar 17, 2020
f704a16
Some minor cleanup
hareeshnagaraj Mar 17, 2020
8fec2b4
SP test now working w/multiplier
hareeshnagaraj Mar 17, 2020
3cc09f0
minor reorg
hareeshnagaraj Mar 17, 2020
e44042f
Multiplier fix
hareeshnagaraj Mar 17, 2020
31b6478
Claim tests now working
hareeshnagaraj Mar 18, 2020
8753335
upgrade test back to working
hareeshnagaraj Mar 18, 2020
81e0608
Compiliing reward transfer, testing now
hareeshnagaraj Mar 20, 2020
6feb5c8
Basic fund now working
hareeshnagaraj Mar 21, 2020
14e8252
CHKPT - still not yet working
hareeshnagaraj Mar 24, 2020
7a2ff45
Savepoint --- still broken
hareeshnagaraj Mar 24, 2020
8b871d5
CHKPT - undelegate works
hareeshnagaraj Mar 24, 2020
0c26a22
Code compiling, not yet tested
hareeshnagaraj Mar 24, 2020
58577f6
Base case w/single delgator working
hareeshnagaraj Mar 24, 2020
d38b3a4
1st two cases validated
hareeshnagaraj Mar 24, 2020
292ab2e
TODO: Expected value verification
hareeshnagaraj Mar 25, 2020
7351a49
Merging working branch
hareeshnagaraj Mar 25, 2020
a7d18ff
all 3 tests passing
hareeshnagaraj Mar 25, 2020
8e07a8e
Functioning slash, validation required however
hareeshnagaraj Mar 25, 2020
32c4826
SLASH in progress
hareeshnagaraj Mar 26, 2020
7b560bd
Slash test still pending, finalizing multiplier initial value
hareeshnagaraj Mar 26, 2020
478e0d0
Fix slash by unstake and transferring out
hareeshnagaraj Mar 26, 2020
e2a811f
Test resolution
hareeshnagaraj Mar 26, 2020
c66f68a
linted delman
hareeshnagaraj Mar 26, 2020
edeb800
linting and all tests fixed
hareeshnagaraj Mar 26, 2020
fa86794
TESTING multiplier w/slash degradation
hareeshnagaraj Mar 27, 2020
f04861b
Fixed slash discrepancy, further testing now
hareeshnagaraj Mar 27, 2020
bf36a5a
test checkpoint
hareeshnagaraj Mar 27, 2020
3e58bad
Checkpointing code ---- MOVING TO ROUNDS BASED CLAIM MODEL
hareeshnagaraj Mar 30, 2020
b5f7146
[eth-contracts] Rounds-based reward model (#348)
hareeshnagaraj Apr 1, 2020
e6aa800
[eth-contracts] Lockup for undelegate operations (#359)
hareeshnagaraj Apr 6, 2020
bcd7833
Merge branch 'hn_eth_contracts_dev' into hn_delegation_working_branch
hareeshnagaraj Apr 6, 2020
889d3ba
[eth-contracts] Valid stake bounds indicator + direct deployer stake …
hareeshnagaraj Apr 9, 2020
4287f94
Finishing touches for v0
hareeshnagaraj Apr 10, 2020
4523b34
RM test
hareeshnagaraj Apr 10, 2020
b3ea273
Merge branch 'hn_eth_contracts_dev' into hn_delegation_working_branch
hareeshnagaraj Apr 10, 2020
00312e4
Generic Governance v1 (Registry integration, DelegationManager integr…
SidSethi Apr 10, 2020
c138bcc
Merge branch 'hn_delegation_working_branch' of github.com:AudiusProje…
hareeshnagaraj Apr 10, 2020
146e6cc
[eth-contracts] Address PR comments (#371)
hareeshnagaraj Apr 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
367 changes: 367 additions & 0 deletions eth-contracts/contracts/Governance.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
pragma solidity ^0.5.0;

import "./service/registry/RegistryContract.sol";
import "./staking/Staking.sol";
import "./service/interface/registry/RegistryInterface.sol";


contract Governance {
RegistryInterface registry;
bytes32 stakingProxyOwnerKey;

uint256 votingPeriod;
uint256 votingQuorum;

/***** Enums *****/
enum Outcome {InProgress, No, Yes, Invalid}
// Enum values map to uints, so first value in Enum always is 0.
enum Vote {None, No, Yes}

struct Proposal {
uint256 proposalId;
address proposer;
uint256 startBlockNumber;
bytes32 targetContractRegistryKey;
address targetContractAddress;
uint callValue;
string signature;
bytes callData;
Outcome outcome;
uint256 voteMagnitudeYes;
uint256 voteMagnitudeNo;
uint256 numVotes;
mapping(address => Vote) votes;
}

/***** Proposal storage *****/
uint256 lastProposalId = 0;
mapping(uint256 => Proposal) proposals;

/***** Events *****/
event ProposalSubmitted(
uint256 indexed proposalId,
address indexed proposer,
uint256 startBlockNumber,
string description
);
event ProposalVoteSubmitted(
uint256 indexed proposalId,
address indexed voter,
Vote indexed vote,
uint256 voterStake,
Vote previousVote
);
event ProposalOutcomeEvaluated(
uint256 indexed proposalId,
Outcome indexed outcome,
uint256 voteMagnitudeYes,
uint256 voteMagnitudeNo,
uint256 numVotes
);
event TransactionExecuted(
bytes32 indexed txHash,
address targetContractAddress,
uint callValue,
string signature,
bytes callData,
bytes returnData
);

constructor(
address _registryAddress,
bytes32 _stakingProxyOwnerKey,
uint256 _votingPeriod,
uint256 _votingQuorum
) public {
require(_registryAddress != address(0x00), "Requires non-zero _registryAddress");
registry = RegistryInterface(_registryAddress);

stakingProxyOwnerKey = _stakingProxyOwnerKey;

require(_votingPeriod > 0, "Requires non-zero _votingPeriod");
votingPeriod = _votingPeriod;

require(_votingQuorum > 0, "Requires non-zero _votingQuorum");
votingQuorum = _votingQuorum;
}

// ========================================= Governance Actions =========================================

function submitProposal(
bytes32 _targetContractRegistryKey,
uint256 _callValue,
string calldata _signature,
bytes calldata _callData,
string calldata _description
) external returns (uint256 proposalId)
{
address proposer = msg.sender;

// Require proposer is active Staker
Staking stakingContract = Staking(registry.getContract(stakingProxyOwnerKey));
require(
stakingContract.totalStakedFor(proposer) > 0,
"Proposer must be active staker with non-zero stake."
);

// Require _targetContractRegistryKey points to a valid registered contract
address targetContractAddress = registry.getContract(_targetContractRegistryKey);
require(
targetContractAddress != address(0x00),
"_targetContractRegistryKey must point to valid registered contract"
);

// set proposalId
uint256 newProposalId = lastProposalId + 1;

// Store new Proposal obj in proposals mapping
proposals[newProposalId] = Proposal({
proposalId: newProposalId,
proposer: proposer,
startBlockNumber: block.number,
targetContractRegistryKey: _targetContractRegistryKey,
targetContractAddress: targetContractAddress,
callValue: _callValue,
signature: _signature,
callData: _callData,
outcome: Outcome.InProgress,
voteMagnitudeYes: 0,
voteMagnitudeNo: 0,
numVotes: 0
/** votes: mappings are auto-initialized to default state */
});

emit ProposalSubmitted(
newProposalId,
proposer,
block.number,
_description
);

lastProposalId += 1;

return newProposalId;
}

function submitProposalVote(uint256 _proposalId, Vote _vote) external {
address voter = msg.sender;

require(
_proposalId <= lastProposalId && _proposalId > 0,
"Must provide valid non-zero _proposalId"
);

// Require voter is active Staker + get voterStake.
Staking stakingContract = Staking(registry.getContract(stakingProxyOwnerKey));
uint256 voterStake = stakingContract.totalStakedForAt(
voter,
proposals[_proposalId].startBlockNumber
);
require(voterStake > 0, "Voter must be active staker with non-zero stake.");

// Require proposal votingPeriod is still active.
uint256 startBlockNumber = proposals[_proposalId].startBlockNumber;
uint256 endBlockNumber = startBlockNumber + votingPeriod;
require(
block.number > startBlockNumber && block.number <= endBlockNumber,
"Proposal votingPeriod has ended"
);

// Require vote is not None.
require(_vote != Vote.None, "Cannot submit None vote");

// Record previous vote.
Vote previousVote = proposals[_proposalId].votes[voter];

// Will override staker's previous vote if present.
proposals[_proposalId].votes[voter] = _vote;

/** Update voteMagnitudes accordingly */

// New voter (Vote enum defaults to 0)
if (previousVote == Vote.None) {
if (_vote == Vote.Yes) {
proposals[_proposalId].voteMagnitudeYes += voterStake;
} else {
proposals[_proposalId].voteMagnitudeNo += voterStake;
}
proposals[_proposalId].numVotes += 1;
} else { // Repeat voter
if (previousVote == Vote.Yes && _vote == Vote.No) {
proposals[_proposalId].voteMagnitudeYes -= voterStake;
proposals[_proposalId].voteMagnitudeNo += voterStake;
} else if (previousVote == Vote.No && _vote == Vote.Yes) {
proposals[_proposalId].voteMagnitudeYes += voterStake;
proposals[_proposalId].voteMagnitudeNo -= voterStake;
}
// If _vote == previousVote, no changes needed to vote magnitudes.
}

emit ProposalVoteSubmitted(
_proposalId,
voter,
_vote,
voterStake,
previousVote
);
}

function evaluateProposalOutcome(uint256 _proposalId)
external returns (Outcome proposalOutcome)
{
require(
_proposalId <= lastProposalId && _proposalId > 0,
"Must provide valid non-zero _proposalId"
);

// Require msg.sender is active Staker.
Staking stakingContract = Staking(registry.getContract(stakingProxyOwnerKey));
require(
stakingContract.totalStakedForAt(
msg.sender, proposals[_proposalId].startBlockNumber
) > 0,
"Caller must be active staker with non-zero stake."
);

// Require proposal votingPeriod has ended.
uint256 startBlockNumber = proposals[_proposalId].startBlockNumber;
uint256 endBlockNumber = startBlockNumber + votingPeriod;
require(
block.number > endBlockNumber,
"Proposal votingPeriod must end before evaluation."
);

// Require registered contract address for provided registryKey has not changed.
address targetContractAddress = registry.getContract(
proposals[_proposalId].targetContractRegistryKey
);
require(
targetContractAddress == proposals[_proposalId].targetContractAddress,
"Registered contract address for targetContractRegistryKey has changed"
);

// Calculate outcome
Outcome outcome;
if (proposals[_proposalId].numVotes < votingQuorum) {
outcome = Outcome.Invalid;
} else if (
proposals[_proposalId].voteMagnitudeYes >= proposals[_proposalId].voteMagnitudeNo
) {
outcome = Outcome.Yes;

_executeTransaction(
proposals[_proposalId].targetContractAddress,
proposals[_proposalId].callValue,
proposals[_proposalId].signature,
proposals[_proposalId].callData
);
} else {
outcome = Outcome.No;
}

// Record outcome
proposals[_proposalId].outcome = outcome;

emit ProposalOutcomeEvaluated(
_proposalId,
outcome,
proposals[_proposalId].voteMagnitudeYes,
proposals[_proposalId].voteMagnitudeNo,
proposals[_proposalId].numVotes
);

return outcome;
}

// ========================================= Getters =========================================

function getProposalById(uint256 _proposalId)
external view returns (
uint256 proposalId,
address proposer,
uint256 startBlockNumber,
bytes32 targetContractRegistryKey,
address targetContractAddress,
uint callValue,
string memory signature,
bytes memory callData,
Outcome outcome,
uint256 voteMagnitudeYes,
uint256 voteMagnitudeNo,
uint256 numVotes
)
{
require(
_proposalId <= lastProposalId && _proposalId > 0,
"Must provide valid non-zero _proposalId"
);

Proposal memory proposal = proposals[_proposalId];
return (
proposal.proposalId,
proposal.proposer,
proposal.startBlockNumber,
proposal.targetContractRegistryKey,
proposal.targetContractAddress,
proposal.callValue,
proposal.signature,
proposal.callData,
proposal.outcome,
proposal.voteMagnitudeYes,
proposal.voteMagnitudeNo,
proposal.numVotes
/** @notice - votes mapping cannot be returned by external function */
);
}

function getVoteByProposalAndVoter(uint256 _proposalId, address _voter)
external view returns (Vote vote)
{
require(
_proposalId <= lastProposalId && _proposalId > 0,
"Must provide valid non-zero _proposalId"
);
return proposals[_proposalId].votes[_voter];
}

// ========================================= Private =========================================

function _executeTransaction(
address _targetContractAddress,
uint256 _callValue,
string memory _signature,
bytes memory _callData
) internal returns (bytes memory /** returnData */)
{
bytes32 txHash = keccak256(
abi.encode(
_targetContractAddress, _callValue, _signature, _callData
)
);

bytes memory callData;

if (bytes(_signature).length == 0) {
callData = _callData;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(_signature))), _callData);
}

(bool success, bytes memory returnData) = (
// solium-disable-next-line security/no-call-value
_targetContractAddress.call.value(_callValue)(callData)
);
require(success, "Governance::executeTransaction:Transaction execution reverted.");

emit TransactionExecuted(
txHash,
_targetContractAddress,
_callValue,
_signature,
_callData,
returnData
);

return returnData;
}
}
4 changes: 3 additions & 1 deletion eth-contracts/contracts/erc20/AudiusToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol";


contract AudiusToken is ERC20, ERC20Detailed, ERC20Mintable, ERC20Pausable {
contract AudiusToken is ERC20, ERC20Detailed, ERC20Mintable, ERC20Pausable, ERC20Burnable {
string constant NAME = "TestAudius";
string constant SYMBOL = "TAUDS";
// standard - imitates relationship between Ether and Wei
Expand All @@ -22,6 +23,7 @@ contract AudiusToken is ERC20, ERC20Detailed, ERC20Mintable, ERC20Pausable {
ERC20Mintable()
// ERC20Detailed provides setters/getters for name, symbol, decimals properties
ERC20Detailed(NAME, SYMBOL, DECIMALS)
// ERC20Burnable has no constructor
ERC20()
public
{
Expand Down