diff --git a/contracts/lib/ethereans/bank/Bank.sol b/contracts/lib/ethereans/bank/Bank.sol index fa27df6..e12081d 100644 --- a/contracts/lib/ethereans/bank/Bank.sol +++ b/contracts/lib/ethereans/bank/Bank.sol @@ -3,8 +3,17 @@ pragma solidity ^0.4.9; contract Bank { - - - - + event Withdrawn(address reciever, uint256 amount); + event Deposited(address sender,uint256 value); + //allow deposit and call event + function deposit() payable { + Deposited(msg.sender, msg.value); + } + + //withdraw if locked and not paid, updates epoch + function withdrawal(address dest, uint amount) + internal { + if(!dest.send(amount)) throw; + Withdrawn(msg.sender, amount); + } } \ No newline at end of file diff --git a/contracts/lib/ethereans/bank/CollaborationBank.sol b/contracts/lib/ethereans/bank/CollaborationBank.sol new file mode 100644 index 0000000..b41ed74 --- /dev/null +++ b/contracts/lib/ethereans/bank/CollaborationBank.sol @@ -0,0 +1,98 @@ +pragma solidity ^0.4.0; + +/** + * Abstract contract used for recieving donations or profits + * Withdraw is divided by total tokens each account owns + * Unlock period allows transfers + * Lock period allow withdraws + * Child contract that implement minting should use modifier not_locked in minting function + * Inspired by ProfitContainer and Lockable by vDice + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./Bank.sol"; +import "../management/EpochLocker.sol"; +import "../token/LockerToken.sol"; + +contract CollaborationBank is Bank, EpochLocker { + + LockableToken public token; + //used for calculating balance and for checking if account withdrawn + uint256 public currentPayEpoch; + //stores the balance from the lock time + uint256 public epochBalance; + //used for hecking if account withdrawn + mapping (address => uint) lastPaidOutEpoch; + //events + event Withdrawn(address tokenHolder, uint256 amountPaidOut); + event Deposited(address donator,uint256 value); + + public CollaborationBank(LockableToken _token) EpochLockable(8 minutes,12 minutes){ + token = _token; + } + + //update the balance and payout epoch + modifier update_epoch { + if(currentPayEpoch < CURRENT_EPOCH) { + currentPayEpoch = _token.CURRENT_EPOCH(); + epochBalance = this.balance; + } + _; + } + + //checks if user already withdrawn + modifier not_paid { + if (lastPaidOutEpoch[msg.sender] == currentPayEpoch) throw; + _; + } + + //check overflow in multiply + function safeMultiply(uint256 _a, uint256 _b) private { + if (!(_b == 0 || ((_a * _b) / _b) == _a)) throw; + _; + } + + //allow deposit and call event + function () + payable { + deposit(); + } + + function setLock(bool _lock){ + token.setLock(_lock); + + } + //withdraw if locked and not paid, updates epoch + function withdrawal() + external + check_lock(true) + update_epoch + not_paid { + uint256 _tokenBalance = token.balanceOf(msg.sender); + uint256 _tokenSupply = token.totalSupply(); + safeMultiply(_tokenBalance, epochBalance); + lastPaidOutEpoch[msg.sender] = currentPayEpoch; + if (this.balance >= epochBalance || _tokenBalance == 0 || _tokenSupply == 0) throw; + super.withdrawal(msg.sender, (_tokenBalance * epochBalance) / _tokenSupply); + } + + //if this coin owns tokens of other CollaborationBank, allow withdraw + function withdrawalFrom(CollaborationBank _otherCollaborationToken) { + _otherCollaborationToken.withdrawal(); + } + + //return expected payout in lock or estimated when not locked + function expectedPayout(address _tokenHolder) + external + constant + returns (uint256 payout) { + if (now < NEXT_LOCK) //unlocked, estimate + payout = (token.balanceOf(_tokenHolder) * this.balance) / token.totalSupply(); + else + payout = (token.balanceOf(_tokenHolder) * epochBalance) / token.totalSupply(); + } + + +} diff --git a/contracts/lib/ethereans/management/EpochLocker.sol b/contracts/lib/ethereans/management/EpochLocker.sol new file mode 100644 index 0000000..1d25737 --- /dev/null +++ b/contracts/lib/ethereans/management/EpochLocker.sol @@ -0,0 +1,44 @@ +pragma solidity ^0.4.8; + +contract EpochLocker is Lockable { + + uint256 public creationTime = now; + uint256 public unlockedTime = 25 days + uint256 public lockedTime = 5 days; + uint256 public constant EPOCH_LENGTH = unlockedTime + lockedTime; + //current epoch constant formula, recalculated in any contract call + uint256 public constant CURRENT_EPOCH = (now - creationTime) / EPOCH_LENGTH + 1; + //next lock constant formula, recalculated in any contract call + uint256 public constant NEXT_LOCK = (creationTime + CURRENT_EPOCH * unlockedTime) + (CURRENT_EPOCH - 1) * lockedTime; + + + function EpochLockable(uint256 _unlockedTime, uint256 _lockedTime){ + unlockedTime = _unlockedTime; + lockedTime = _lockedTime; + } + + //update lock value if needed or throw if unexpected lock + modifier check_lock(bool lockedOnly) { + if(lockedOnly){ //method allowed when locked + if (NEXT_LOCK < now) { //is locked! + if (!lock) setLock(true); //storage says other thing, update it. + } + else { //is not locked! + if (!lock) throw; //unlocked and storage already say so, throw to prevent event flood. + setLock(false); //update storage + return; //prevent method from running post states. + } + }else{ //method allowed when unlocked. + if (NEXT_LOCK < now) { //is locked! + if (lock) throw; //no need to update storage. + setLock(true);//update storage + return; //prevent method from running post states. + } + else { //is not locked! + if (lock) setLock(false); //storage says other thing, update it. + } + } + } + + +} \ No newline at end of file diff --git a/contracts/lib/ethereans/management/Lockable.sol b/contracts/lib/ethereans/management/Lockable.sol new file mode 100644 index 0000000..eae6ef9 --- /dev/null +++ b/contracts/lib/ethereans/management/Lockable.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.4.8; + +import "./Owned.sol"; + +contract Lockable { + bool public lock = true; + event Locked(bool lock); + + fuction setLock(bool _lock) internal { + Locked(_lock); + lock = _lock; + } + +} \ No newline at end of file diff --git a/contracts/lib/ethereans/token/CollaborationToken.sol b/contracts/lib/ethereans/token/CollaborationToken.sol deleted file mode 100644 index 6a44312..0000000 --- a/contracts/lib/ethereans/token/CollaborationToken.sol +++ /dev/null @@ -1,158 +0,0 @@ -pragma solidity ^0.4.0; - -/** - * Abstract contract used for recieving donations or profits - * Withdraw is divided by total tokens each account owns - * Unlock period allows transfers - * Lock period allow withdraws - * Child contract that implement minting should use modifier not_locked in minting function - * Inspired by ProfitContainer and Lockable by vDice - * - * By Ricardo Guilherme Schmidt - * Released under GPLv3 License - */ - -import "./AbstractToken.sol"; - -contract CollaborationToken is AbstractToken { - //creation time, defined when contract is created - uint256 public creationTime = now; - //time constants, defines epoch size and periods - uint256 public constant UNLOCKED_TIME = 25 days; - uint256 public constant LOCKED_TIME = 5 days; - uint256 public constant EPOCH_LENGTH = UNLOCKED_TIME + LOCKED_TIME; - //current epoch constant formula, recalculated in any contract call - uint256 public constant CURRENT_EPOCH = (now - creationTime) / EPOCH_LENGTH + 1; - //next lock constant formula, recalculated in any contract call - uint256 public constant NEXT_LOCK = (creationTime + CURRENT_EPOCH * UNLOCKED_TIME) + (CURRENT_EPOCH - 1) * LOCKED_TIME; - //used for calculating balance and for checking if account withdrawn - uint256 public currentPayEpoch; - //stores the balance from the lock time - uint256 public epochBalance; - //stores lock state, used for events - bool public lock; - //used for hecking if account withdrawn - mapping (address => uint) lastPaidOutEpoch; - //events - event Withdrawn(address tokenHolder, uint256 amountPaidOut); - event Deposited(address donator,uint256 value); - event Locked(); - event Unlocked(); - - //checks if not locked and call event on change - modifier not_locked { - if (NEXT_LOCK < now) { - if (lock) throw; - lock = true; - Locked(); - return; - } - else { - if (lock) { - lock = false; - Unlocked(); - } - } - _; - } - - //checks if is locked and call event on change - modifier locked { - if (NEXT_LOCK < now) { - if (!lock){ - lock = true; - Locked(); - } - } - else { - if (!lock) throw; - lock = false; - Unlocked(); - return; - } - _; - } - - //update the balance and payout epoch - modifier update_epoch { - if(currentPayEpoch < CURRENT_EPOCH) { - currentPayEpoch = CURRENT_EPOCH; - epochBalance = this.balance; - } - _; - } - - //checks if user already withdrawn - modifier not_paid { - if (lastPaidOutEpoch[msg.sender] == currentPayEpoch) throw; - _; - } - - //check overflow in multiply - modifier safe_multiply(uint256 _a, uint256 _b) { - if (!(_b == 0 || ((_a * _b) / _b) == _a)) throw; - _; - } - - //allow deposit and call event - function () - payable { - Deposited(msg.sender, msg.value); - } - - //withdraw if locked and not paid, updates epoch - function withdrawal() - external - locked - update_epoch - not_paid - safe_multiply(balanceOf(msg.sender), epochBalance) { - uint256 _currentEpoch = CURRENT_EPOCH; - uint256 _tokenBalance = balanceOf(msg.sender); - uint256 _totalSupply = totalSupply; - if (this.balance == 0 || _tokenBalance == 0) throw; - lastPaidOutEpoch[msg.sender] = currentPayEpoch; - uint256 amountToPayOut = (_tokenBalance * epochBalance) / _totalSupply; - if(!msg.sender.send(amountToPayOut)) { - throw; - } - Withdrawn(msg.sender, amountToPayOut); - } - - //if this coin owns tokens of other lockablecoin, allow withdraw - function withdrawalFrom(CollaborationToken _otherCollaborationToken) { - _otherCollaborationToken.withdrawal(); - } - - //return expected payout in lock or estimated when not locked - function expectedPayout(address _tokenHolder) - external - constant - returns (uint256 payout) { - if (now < NEXT_LOCK) //unlocked, estimate - payout = (balanceOf(_tokenHolder) * this.balance) / totalSupply; - else - payout = (balanceOf(_tokenHolder) * epochBalance) / totalSupply; - } - - //overwrite not allow transfer during lock - function transfer(address _to, uint256 _value) - not_locked - returns (bool ok) { - return super.transfer(_to,_value); - } - - //overwrite not allow transfer during lock - function transferFrom(address _from, address _to, uint256 _value) - not_locked - returns (bool ok) { - return super.transferFrom(_from,_to,_value); - } - - //overwrite not allow transfer during lock - function approve(address _spender, uint256 _value) - returns (bool ok) { - return super.approve(_spender,_value); - } - -} diff --git a/contracts/lib/ethereans/token/LockerToken.sol b/contracts/lib/ethereans/token/LockerToken.sol new file mode 100644 index 0000000..89425b9 --- /dev/null +++ b/contracts/lib/ethereans/token/LockerToken.sol @@ -0,0 +1,49 @@ +pragma solidity ^0.4.0; + +/** + * Abstract contract used for recieving donations or profits + * Withdraw is divided by total tokens each account owns + * Unlock period allows transfers + * Lock period allow withdraws + * Child contract that implement minting should use modifier not_locked in minting function + * Inspired by ProfitContainer and Lockable by vDice + * + * By Ricardo Guilherme Schmidt + * Released under GPLv3 License + */ + +import "./AbstractToken.sol"; +import "../management/Lockable.sol"; + +contract LockerToken is AbstractToken, Lockable, Owned { + Lockable public locker = this; + + + function unlinkLocker() only_owner { + locker = this; + } + function linkLocker(Lockable _locker) only_owner { + locker = _locker; + } + function lock(bool _lock) only_owner { + setLock(_lock); + } + + modifier when_locked(bool value){ + if (lock != value && locker.lock() != value) throw; + _; + } + + //overwrite not allow transfer during lock + function transfer(address _to, uint256 _value) when_locked(true); + returns (bool ok) { + return super.transfer(_to,_value); + } + + //overwrite not allow transfer during lock + function transferFrom(address _from, address _to, uint256 _value) + returns (bool ok) when_locked(true) { + return super.transferFrom(_from,_to,_value); + } + +} diff --git a/contracts/src/BountyBank.sol b/contracts/src/BountyBank.sol new file mode 100644 index 0000000..ef04915 --- /dev/null +++ b/contracts/src/BountyBank.sol @@ -0,0 +1,63 @@ +pragma solidity ^0.4.9; + +import "lib/ethereans/management/Owned.sol" + +contract BountyBank is Owned { + + enum State {CLOSED, OPEN, CLAIMED}; + struct Bounty { + State state; + uint closedAt; + mapping (address => int) deposits; + mapping (address => int) claimers; + uint balance; + uint points; + } + + mapping (uint => Bounty) bounties; + uint count = 0; + + function deposit(int num) payable { + if(bounties[num].state != OPEN || msg.value == 0) throw; + bounties[num].deposits[msg.sender] += msg.value; + bounties[num].balance += msg.value; + } + + function withdraw(int num) { + uint value = bounties[num].deposits[msg.sender]; + if(bounties[num].state != OPEN || value == 0) throw; + delete bounties[num].deposits[msg.sender]; + if(!msg.sender.send(value)) throw; + } + + function open(uint num) only_owner { + if(bounties[num].claimed == true) throw; + bounties[num].state = State.OPEN; + } + + function setClaimer(uint num, address claimer, uint points) only_owner { + if(bounties[num].state == State.CLAIMED) throw; + bounties[num].claimers[claimer] += points; + bounties[num].points += points; + } + + function close(uint num) only_owner { + if(bounties[num].state == State.CLAIMED) throw; + bounties[num].close = true; + bounties[num].closedAt = now; + } + + function claim(uint num){ + if (bounties[num].state == State.OPEN) throw; + uint totalPoints = bounties[num].points; + if(totalPoints == 0) throw; + uint points = bounties[num].claimers[msg.sender]; + if (points == 0) throw; + delete bounties[num].claimers[msg.sender]; + uint award = (bounties[num].balance / totalPoints)*points; + bounties[num].points -= points; + bounties[num].balance -= award; + if(!msg.sender.send(award)) throw; + } + + } \ No newline at end of file diff --git a/contracts/src/AbstractBounty.sol b/contracts/src/GitBountyBank.sol similarity index 100% rename from contracts/src/AbstractBounty.sol rename to contracts/src/GitBountyBank.sol diff --git a/contracts/src/git-repository/GitRepository.sol b/contracts/src/git-repository/GitRepository.sol index cf15a3a..75ced84 100644 --- a/contracts/src/git-repository/GitRepository.sol +++ b/contracts/src/git-repository/GitRepository.sol @@ -15,6 +15,7 @@ pragma solidity ^0.4.8; * Released under GPLv3 License */ +import "lib/ethereans/bank/CollaborationBank.sol"; import "lib/ethereans/management/Owned.sol"; import "./GitRepositoryI.sol"; import "./GitRepositoryToken.sol"; @@ -25,6 +26,9 @@ contract GitRepository is GitRepositoryI, Owned { //Address of the oracle, used for github login address lookup GitRepositoryStorage public db; GitRepositoryToken public token; + CollaborationBank public donationBank; + BountyBank + mapping (address=>uint) beers; uint256 public subscribers; uint256 public watchers; @@ -37,9 +41,17 @@ contract GitRepository is GitRepositoryI, Owned { _; } + function () payable { + donationBank.deposit(); + } + + + function GitRepository(uint256 _uid, string _name) { db = new GitRepositoryStorage(_uid,_name); token = new GitRepositoryToken(_name); + donationBank = new CollaborationBank(token); + token.linkLocker(donationBank); } //checks if a commit is already claimed diff --git a/contracts/src/git-repository/GitRepositoryToken.sol b/contracts/src/git-repository/GitRepositoryToken.sol index 7b7a9c6..1cb70ff 100644 --- a/contracts/src/git-repository/GitRepositoryToken.sol +++ b/contracts/src/git-repository/GitRepositoryToken.sol @@ -15,10 +15,10 @@ pragma solidity ^0.4.8; * Released under GPLv3 License */ -import "lib/ethereans/token/CollaborationToken.sol"; +import "lib/ethereans/token/LockerToken.sol"; import "lib/ethereans/management/Owned.sol"; -contract GitRepositoryToken is CollaborationToken, Owned { +contract GitRepositoryToken is LockerToken { function GitRepositoryToken(string _repository) { setAttribute("name", _repository); @@ -27,7 +27,7 @@ contract GitRepositoryToken is CollaborationToken, Owned { } function mint(address _who, uint256 _value) - only_owner { + only_owner when_locked(false)) { _mint(_who,_value); }