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

Modular contracts for easy upgrades #59

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

import "zeppelin-solidity/contracts/ownership/Ownable.sol";

interface authorizedStakingContract {
function stakeBalanceOf(address addr) public returns (uint256);
}


/**
* @title Staking Proxy Contract
* @dev An ownable staking proxy contract to provide upgradable staking.
* Upgraded contract can be authorized as an active contract by the owner.
* The old contracts are put into legacyContracts array.
* The staking contracts must call emit events on this contract.
*/
contract StakingProxy is Ownable {
/**
* @dev Only authorized contracts can invoke functions with this modifier.
*/
modifier onlyAuthorized {
require(isAuthorized(msg.sender));
_;
}

address public activeContract;
address[] public legacyContracts;

event Staked(address indexed user, uint256 amount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the Go interface I also need a 2nd Event StakedAny (or some more appropriate name) that leaves out the "indexed" on the "user"

event StakedAny(address user, uint256 amount)

The abigen Go code will not let me catch the Staked event (with address indexed user unless I know the address of the user. My understanding of the higher level code means that it will never know the address of the user until the event happens. This is a circular loop. (((If you find a way to get the Go code to listen for events like this w/o removing the "indexed" I am totally for just the one event)))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key problem here is the indexed, because it's used for filtering, right? My reading of the eth_newFilter docs seems to indicate we should be able to ignore the indexed stuff (topics) with the right parameters, no? Or is it just that the generated Go code can't specify a null for the topic?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried using NULL for the filter and I never get back the event. Inside geth the bloom filter is different when I use NULL than if I setup the same test from node.js - This may indicate that I am not doing it right - or that there is a defect in Geth or Abigen.

event Unstaked(address indexed user, uint256 amount);

/**
* @dev Gets the sum of all staking balances of the specified staker address.
* @param _staker The address to query the balance of.
* @return An uint256 representing the amount staked by the passed address.
*/
function balanceOf(address _staker)
public
constant
returns (uint256 _balance)
{
uint256 balance = authorizedStakingContract(activeContract).stakeBalanceOf(_staker);
for (uint i = 0; i < legacyContracts.length; i++) {
balance = balance + authorizedStakingContract(legacyContracts[i]).stakeBalanceOf(_staker);
}
return balance;
}

/**
* @dev Update active contract.
* @param _contract The address of a staking contract.
*/
function updateActiveContract(address _contract)
public
onlyOwner
{
require(_contract != address(0));
require(_contract != activeContract);
legacyContracts.push(activeContract);
activeContract = _contract;
}

/**
* @dev Emit staked event.
* @param _staker The address of the staker.
* @param _amount The staked amount.
*/
function emitStakedEvent(address _staker, uint256 _amount)
public
onlyAuthorized
{
Staked(_staker, _amount);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 2nd Event could just be added at this point. There are some tradeoffs. Is it better to have the proxy think and not do any work - or better to guarantee that both events always happen?

}

/**
* @dev Emit unstaked event.
* @param _staker The address of the staker.
* @param _amount The unstaked amount.
*/
function emitUnstakedEvent(address _staker, uint256 _amount)
public
onlyAuthorized
{
Unstaked(_staker, _amount);
}

/**
* @dev Check if a staking contract is authorized to work with this contract.
* @param _address The address of a staking contract.
* @return A bool wether it's authorized.
*/
function isAuthorized(address _address)
public
returns (bool)
{
if (_address == activeContract) {
return true;
}
for (uint i = 0; i < legacyContracts.length; i++) {
if (legacyContracts[i] == _address) {
return true;
}
}
return false;
}
}
Loading