Files

220 lines
6.7 KiB
Solidity

pragma solidity ^0.4.11;
import "../bank/TokenBank.sol";
import "../management/Controlled.sol";
/**
* @title IssueBank
* @author Ricardo Guilherme Schmidt
* Enable deposits to be withdrawn by points recievers
**/
contract IssueBank is Controlled, TokenBank {
enum State {OPEN, REFUND, REWARD, FINALIZED }
State public state;
uint public points;
uint public refundNonce;
mapping (address => Reward) public rewards;
address public repoOwner;
struct Reward {
bool active;
uint points;
}
modifier onlyRepoOwner{
if (msg.sender != repoOwner) throw;
_;
}
function IssueBank(address _repoOwner) {
repoOwner = _repoOwner;
state = State.OPEN;
}
/**
* @notice deposit ether in bank
**/
function () payable {
depositEther(new bytes(0));
}
/**
* @notice claim all watched tokens based on user points;
*/
function reward() {
_reward(tokens);
}
/**
* @notice Useful if some token is throwing on transfer.
* @param _tokens the array of desired watched tokens. If token is not watched it will ignore..
*/
function rewardEthAnd(address[] _tokens) {
_reward(_tokens);
}
/**
* @notice only contoller may set reward to a single address.
* @param _claimer the beneficiary
* @param _points amount of points
**/
function setReward(address _claimer, uint _points) onlyController {
if(state != State.OPEN) throw;
if(rewards[_claimer].active) throw;
rewards[_claimer].points = _points;
}
/**
* @notice only contoller may set reward to an array of addresses.
* @param _claimers the array of beneficiaries
* @param _points the array of amount of points
**/
function setReward(address[] _claimers, uint[] _points) onlyController {
if(state != State.OPEN) throw;
uint len = _claimers.length;
for (uint i = 0; i < len; i++){
address _claimer = _claimers[i];
if(rewards[_claimer].active) throw;
rewards[_claimer].points = _points[i];
}
}
/**
* @notice only the repo owner may confirm reward of addresses
* @param _claimers array of addresses that are eligible to reward
**/
function confirm(address [] _claimers) onlyRepoOwner {
uint len = _claimers.length;
uint nPoints = 0;
for (uint i = 0; i < len; i++){
address _claimer = _claimers[i];
if(rewards[_claimer].active) throw;
rewards[_claimer].active = true;
nPoints += rewards[_claimer].points;
}
points += nPoints;
}
/**
* @notice only the repo owner may confirm reward of single address
* @param _claimer the address that is eligible to reward
**/
function confirm(address _claimer) onlyRepoOwner {
if(rewards[_claimer].active) throw;
rewards[_claimer].active = true;
points += rewards[_claimer].points;
}
/**
* @notice only repo owner can close deposits and start reward or refund
* If no points confirmed the system will start refund, otherwise reward
*/
function close() onlyRepoOwner {
if(state != State.OPEN) throw;
state = points > 0 ? State.REWARD : State.REFUND;
}
/**
* @notice Set a new list of tokens to be rewarded.
* User can still reward tokens not listed if he calls `rewardEthAnd(address[]`
* providing a list including unlisted tokens.
* To list a new token use `watch(address)` that will update the balance aswell
* @param _tokens the list of new tokens.
*
*/
function setTokenList(address[] _tokens) onlyController {
tokens = _tokens;
}
/**
* @notice Withdraw tokens
* only avaliable in REWARD state and all points claimed.
*
**/
function withdraw(address[] _tokens) onlyController {
if(state != State.FINALIZED) throw;
uint len = _tokens.length;
uint amount;
for (uint i = 0; i< len; i++){
ERC20 token = ERC20(_tokens[i]);
amount = token.balanceOf(this);
if(amount > 0) _withdraw(token, repoOwner, amount, 0x0);
}
}
/**
* @notice only controller may kill contract and send all remaining eth and tokens to repo owner
* This can only be done when all rewards are claimed.
**/
function kill() onlyController {
if(state != State.FINALIZED) throw;
withdraw(tokens);
selfdestruct(repoOwner);
}
/**
* @dev overwriten to only allow refund in correct state.
**/
function refund(address token) returns (bool) {
if(state != State.REFUND) throw;
return super.refund(token);
}
/**
* @dev register the deposit to refundings
**/
function _deposited(address _sender, uint _amount, address _tokenAddr, bytes _data)
internal {
if(state != State.OPEN) throw;
super._deposited(_sender, _amount, _tokenAddr, _data);
}
function _reward(address[] _tokens) internal {
if(state != State.REWARD) throw;
address dest = msg.sender;
if (!rewards[dest].active) throw;
uint _reward_points = rewards[dest].points;
delete rewards[dest];
if (_reward_points == 0) throw;
uint reward;
uint len = _tokens.length;
for (uint i = 0; i< len; i++){
address tokenAddr = _tokens[i];
reward = tokenBalances[tokenAddr];
if (reward > 0) reward = calculeReward(reward, _reward_points);
if (reward > 0) _withdraw(tokenAddr, dest, reward, 0x0);
}
reward = this.balance;
if (reward > 0) reward = (reward / points) * _reward_points;
if (reward > 0) _withdraw(0x0, dest, reward, 0x0);
points -= _reward_points;
if(points == 0) state = State.FINALIZED;
}
/**
* @dev amplifies small token balances to divide points
**/
function calculeReward(uint _balance, uint _reward_points) internal constant returns (uint reward) {
uint amplifier = 1;
while(_balance * amplifier < points){
amplifier *= 10;
}
reward = (((_balance*amplifier) / points) * _reward_points) / amplifier;
}
}
/**
* @title IssueBankFactory
* @author Ricado Guilherme Schmidt <3esmit>
**/
contract IssueBankFactory {
/**
* @notice creates new IssueBank with repoOwner as moderator
**/
function create(address repoOwner) returns(IssueBank){
IssueBank bank = new IssueBank(repoOwner);
bank.changeController(msg.sender);
return bank;
}
}