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

Split into smaller components + create snapshots on demand. #28

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fabd204
Each contract their own file
Aug 11, 2017
5bec4a1
Add import statements
Aug 11, 2017
0be4b8f
Upgrade to solidity ^0.4.13
Aug 11, 2017
bce811f
Add Snapshot contract
Aug 11, 2017
b5f0819
Use Snapshot in MiniMeToken
Aug 11, 2017
b36beb7
Optimize Snapshot values
Aug 11, 2017
674c466
Add SnapshotTokenBase
Aug 11, 2017
a124eb0
Add interfaces
Aug 11, 2017
c752cbc
Add Helpers
Aug 11, 2017
d90f933
Fork from parent token
Aug 11, 2017
e1b2c5e
Add AllowanceBase
Aug 11, 2017
6763b38
Use interfaces and bases
Aug 11, 2017
dbb6eee
Factor out controller claims
Aug 11, 2017
d0fd09b
Sort function order
Aug 11, 2017
a591d08
Merge pull request #1 from Neufund/refactor
recmo Aug 11, 2017
47289d3
Mixins for snapshots mechanisms
Aug 21, 2017
d932eaf
Use SnapshotDailyHybrid
Aug 21, 2017
c6e5cf1
Remove test code
Aug 21, 2017
3141cbb
Refactor mixins
Aug 22, 2017
968ac73
Use refactored snapshots
Aug 22, 2017
d92e58d
Merge pull request #5 from Neufund/snapshotMixin
recmo Aug 22, 2017
0290e9a
Interface IApproveAndCallFallback
Aug 13, 2017
057d39d
Add Disbursal contract
Aug 13, 2017
4219c43
Allow out of order claims
Aug 13, 2017
7af979e
Sort files in folders
Aug 22, 2017
ff5bca9
Remove factory and clone
Aug 22, 2017
c56eb85
remove build files from source folder
Aug 22, 2017
5e2b5c0
Merge pull request #2 from Neufund/disbursal
recmo Aug 22, 2017
b3df899
Add Vote contract
Aug 13, 2017
c880a68
Fix disbursal contract
Aug 22, 2017
edcb2cd
Fix voting contract
Aug 22, 2017
da209bf
Merge pull request #6 from Neufund/voting
recmo Aug 22, 2017
d8dc772
Rename to SnapshotToken
Aug 22, 2017
ade281a
Merge pull request #7 from Neufund/publish
recmo Aug 22, 2017
51e05d0
Rename mTransfer
Aug 22, 2017
7223df1
Fix constructor name
Aug 22, 2017
e84d3cc
Always create log event
Aug 22, 2017
12e76ec
Remove unused log event
Aug 22, 2017
d2fdd4e
Fix modification check
Aug 22, 2017
0d0a840
No nested expressions
Aug 22, 2017
7af6536
Don't assume continuity accross fork
Aug 22, 2017
2a75588
Add IsContract
Aug 22, 2017
ed9989c
Version bumb
Aug 22, 2017
303f0b3
Merge pull request #8 from Neufund/fixes
recmo Aug 22, 2017
71a2ce7
Turn all interfaces into contracts
Aug 23, 2017
7c95aaf
Increment version
Aug 23, 2017
bb94841
Merge pull request #9 from Neufund/drop-interface
recmo Aug 23, 2017
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
30 changes: 30 additions & 0 deletions contracts/Controlled.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
pragma solidity ^0.4.13;

import './ITokenController.sol';

contract Controlled {

ITokenController public controller;

/// @notice The address of the controller is the only address that can call
/// a function with this modifier
modifier onlyController
{
require(msg.sender == address(controller));
_;
}

function Controlled()
{
controller = ITokenController(msg.sender);
}

/// @notice Changes the controller of the contract
/// @param _newController The new controller of the contract
function changeController(ITokenController _newController)
public
onlyController
{
controller = _newController;
}
}
38 changes: 38 additions & 0 deletions contracts/ControllerClaims.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
pragma solidity ^0.4.13;

import './Controlled.sol';
import './Standards/IBasicToken.sol';

contract ControllerClaims is Controlled {

////////////////
// Events
////////////////

event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);

event ClaimedOwnership(address indexed _owned, address indexed _controller);

///////////////////
// Public functions
///////////////////

/// @notice This method can be used by the controller to extract mistakenly
/// sent tokens to this contract.
/// @param _token The address of the token contract that you want to recover
/// set to 0 in case you want to extract ether.
function claimTokens(IBasicToken _token)
public
onlyController
{
// Transfer Ether
if (address(_token) == 0) {
controller.transfer(this.balance);
return;
}

uint balance = _token.balanceOf(this);
_token.transfer(controller, balance);
ClaimedTokens(_token, controller, balance);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.4.6;
pragma solidity ^0.4.13;

/*
Copyright 2017, Jordi Baylina
Expand Down
251 changes: 251 additions & 0 deletions contracts/Extensions/Disbursal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
pragma solidity ^0.4.13;

import '../Standards/ISnapshotToken.sol';
import '../Standards/IBasicToken.sol';
import '../Standards/IERC20Token.sol';
import '../Standards/IApproveAndCallFallback.sol';

// TODO: Anyone can create a token and disburse it, but then everyone
// needs to pay extra gas for claim(). It is not possible to skip
// these mallicious disbursals. Some solution strategies:
// * Limit the people who can disburse to a trusted set
// * Allow claims in any order

contract Disbursal is IApproveAndCallFallback {

////////////////
// Types
////////////////

struct Disbursment {
uint256 snapshot;
IBasicToken disbursedToken;
uint256 remainingAmount;
uint256 remainingShares;
}

////////////////
// State
////////////////

ISnapshotToken public SHARE_TOKEN;

Disbursment[] disbursments;

mapping(address => mapping(uint256 => bool)) claimed;

////////////////
// Events
////////////////

event Disbursed(
uint256 disbursalIndex,
IBasicToken disbursedToken,
uint256 amount,
uint256 snapshot,
uint256 totalShares
);

event Claimed(
address beneficiary,
uint256 disbursalIndex,
IBasicToken disbursedToken,
uint256 amount
);

////////////////
// Constructor
////////////////

function Disbursal(
ISnapshotToken shareToken
)
public
{
SHARE_TOKEN = shareToken;
}

////////////////
// Public functions
////////////////

function claimables(address beneficiary, uint256 from)
public
constant
returns (uint256[100])
{
uint256[100] memory result;
uint j = 0;
for (uint256 i = from; i < disbursments.length; ++i) {
if (claimable(beneficiary, i)) {
result[j] = i;
j += 1;
if (j == 100) {
break;
}
}
}
return result;
}

function claimable(address beneficiary, uint256 index)
public
constant
returns (bool)
{
// Invalid index
if(index >= disbursments.length) {
return false;
}

// Already claimed
if(claimed[beneficiary][index] == true) {
return false;
}

// Check if `beneficiary` has shares
Disbursment storage disbursment = disbursments[index];
uint256 shares = SHARE_TOKEN.balanceOfAt(beneficiary, disbursment.snapshot);
return shares > 0;
}

function claim()
public
{
claim(msg.sender);
}

function claim(address beneficiary)
public
{
for(uint256 i = 0; i < disbursments.length; ++i) {
if(claimed[beneficiary][i] == false) {
claim(beneficiary, i);
}
}
}

function claim(address beneficiary, uint256[] indices)
public
{
for(uint256 i = 0; i < indices.length; ++i) {
claim(beneficiary, indices[i]);
}
}

function claim(address beneficiary, uint256 index)
public
{
require(index < disbursments.length);
require(claimed[beneficiary][index] == false);

// Compute share
// NOTE: By mainting both remaining counters we have automatic
// distribution of rounding errors between claims, with the
// final claim being exact and issuing all the remaining tokens.
// TODO: Remove < 2¹²⁸ restrictions
// TODO: Correct rounding instead of floor.
Disbursment storage disbursment = disbursments[index];
uint256 shares = SHARE_TOKEN.balanceOfAt(beneficiary, disbursment.snapshot);
assert(disbursment.remainingShares < 2**128);
assert(disbursment.remainingAmount < 2**128);
assert(shares <= disbursment.remainingShares);
uint256 amount = mulDiv(disbursment.remainingAmount, shares, disbursment.remainingShares);
assert(amount <= disbursment.remainingAmount);

// Update state
// TODO: Can we reduce the number of state writes?
disbursment.remainingAmount -= amount;
disbursment.remainingShares -= shares;
claimed[beneficiary][index] = true;

// Transfer tokens
IBasicToken token = disbursment.disbursedToken;
bool success = token.transfer(beneficiary, amount);
require(success);

// Log and return
Claimed(beneficiary, index, token, amount);
}

function disburseAllowance(IERC20Token token)
public
{
uint256 amount = token.allowance(msg.sender, this);
disburseAllowance(token, msg.sender, amount);
}

function disburseAllowance(IERC20Token token, address from, uint256 amount)
public
{
// Transfer all allowed tokens to self.
require(amount < 2**128);
bool success = token.transferFrom(from, this, amount);
require(success);

// Disburse these tokens
disburse(IBasicToken(token), amount);
}

// ERC20 receiver
function receiveApproval(address from, uint256 amount, IERC20Token token, bytes data)
public
{
require(data.length == 0);
disburseAllowance(token, from, amount);
}

// TODO: ERC223 style receiver

////////////////
// Internal functions
////////////////

// TODO: Ideally we make this function public, and allow
// disbursal of any basic token. When counting how
// many tokens we need to disburse, a simple
// `balanceOf(this)` is insufficient, as it also
// contains the remaining amount from previous disbursments.
function disburse(IBasicToken token, uint256 amount)
internal
{
// Transfer all allowed tokens to self.
require(amount > 0);
require(amount < 2**128);

// Verify our balance
// TODO: we need to check for newly received tokens!

// Create snapshot
uint256 snapshot = SHARE_TOKEN.createSnapshot();
uint256 totalShares = SHARE_TOKEN.totalSupplyAt(snapshot);
require(totalShares < 2**128);

// Create disbursal
uint256 index = disbursments.length;
disbursments.push(Disbursment({
snapshot: snapshot,
disbursedToken: token,
remainingAmount: amount,
remainingShares: totalShares
}));

// Log
Disbursed(index, token, amount, snapshot, totalShares);
}

function mulDiv(
uint256 value,
uint256 numerator,
uint256 denominator
)
internal
constant
returns (uint256)
{
require(value < 2**128);
require(numerator < 2**128);
require(numerator <= denominator);
return (value * numerator) / denominator;
}
}
59 changes: 59 additions & 0 deletions contracts/Extensions/Vote.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
pragma solidity ^0.4.13;

import '../Standards/ISnapshotToken.sol';

// https://en.wikipedia.org/wiki/Comparison_of_electoral_systems

// https://en.wikipedia.org/wiki/Arrow%27s_impossibility_theorem
// https://en.wikipedia.org/wiki/Gibbard%E2%80%93Satterthwaite_theorem

// * Votes are public
// * Voting is weighed by amount of tokens owned
// * Votes can be changed
// *

// Cardinal systems are a natural fit for a token based voting system.
// * https://en.wikipedia.org/wiki/Approval_voting
// * https://en.wikipedia.org/wiki/Majority_judgment
// → https://en.wikipedia.org/wiki/Range_voting

// TODO: Implement Range voting with:
// * Votes proportional to shares (i.e. one vote per share)
// * Proxy voting: ability to delegate voting power
// * Ability to trade voting power (is this the same as above?)

// TODO:

contract Vote {

ISnapshotToken public TOKEN;
uint256 public SNAPSHOT;
bytes32[] public CHOICE_HASHES;

string[] public choices;
uint256[] totals;

// Note: we use hashes because Solidity currently does not support passing
// string[] as an argument for external functions.
function Vote(
ISnapshotToken token,
bytes32[] choiceHashes
) {
TOKEN = token;
SNAPSHOT = token.createSnapshot();
CHOICE_HASHES = choiceHashes;
choices.length = CHOICE_HASHES.length;
}

function initChoice(uint index, string choice)
{
require(index < CHOICE_HASHES.length);
require(keccak256(choice) == CHOICE_HASHES[index]);
choices[index] = choice;
}

function vote(uint256[] votes)
public
{
}
}
Loading