aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/ethereum/serpent-go/serpent/examples/mutuala.se
blob: 3efb0edebba6ef51cd4e62e7384015b16216b949 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# mutuala - subcurrency

# We want to issue a currency that reduces in value as you store it through negative interest.
# That negative interest would be stored in a commons account.  It's like the p2p version of a
# capital tax

# the same things goes for transactions - you pay as you use the currency.  However, the more
# you pay, the more you get to say about what the tax is used for

# each participant can propose a recipient for a payout to be made out of the commons account,
# others can vote on it by awarding it tax_credits.

# TODO should proposal have expiration timestamp?, after which the tax_credits are refunded
# TODO multiple proposals can take more credits that available in the Commons, how to handle this
# TODO how to handle lost accounts, after which no longer possible to get 2/3 majority

shared:
    COMMONS = 42
    ADMIN = 666
    CAPITAL_TAX_PER_DAY = 7305 # 5% per year
    PAYMENT_TAX = 20 # 5%

    ACCOUNT_LIST_OFFSET = 2^160
    ACCOUNT_MAP_OFFSET = 2^161
    PROPOSAL_LIST_OFFSET = 2^162
    PROPOSAL_MAP_OFFSET = 2^163

init:
    contract.storage[ADMIN] = msg.sender
    contract.storage[ACCOUNT_LIST_OFFSET - 1] = 1
    contract.storage[ACCOUNT_LIST_OFFSET] = msg.sender
    contract.storage[ACCOUNT_MAP_OFFSET + msg.sender] = 10^12
    contract.storage[ACCOUNT_MAP_OFFSET + msg.sender + 1] = block.timestamp

# contract.storage[COMMONS] = balance commons

# contract.storage[ACCOUNT_LIST_OFFSET - 1] = number of accounts
# contract.storage[ACCOUNT_LIST_OFFSET + n] = account n

# contract.storage[PROPOSAL_LIST_OFFSET - 1] contains the number of proposals
# contract.storage[PROPOSAL_LIST_OFFSET + n] = proposal n

# per account:
# contract.storage[ACCOUNT_MAP_OFFSET + account] = balance
# contract.storage[ACCOUNT_MAP_OFFSET + account+1] = timestamp_last_transaction
# contract.storage[ACCOUNT_MAP_OFFSET + account+2] = tax_credits

# per proposal:
# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = recipient
# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id+1] = amount
# contract.storage[PROPOSAL_MAP_OFFSET + proposal_id+2] = total vote credits

code:
    if msg.data[0] == "suicide" and msg.sender == contract.storage[ADMIN]:
        suicide(msg.sender)

    elif msg.data[0] == "balance":
        addr = msg.data[1]
        return(contract.storage[ACCOUNT_MAP_OFFSET + addr])

    elif msg.data[0] == "pay":
        from = msg.sender
        fromvalue = contract.storage[ACCOUNT_MAP_OFFSET + from]
        to = msg.data[1]
        if to == 0 or to >= 2^160:
            return([0, "invalid address"], 2)
        value = msg.data[2]
        tax = value / PAYMENT_TAX

        if fromvalue >= value + tax:
            contract.storage[ACCOUNT_MAP_OFFSET + from] = fromvalue - (value + tax)
            contract.storage[ACCOUNT_MAP_OFFSET + to] += value
            # tax
            contract.storage[COMMONS] += tax
            contract.storage[ACCOUNT_MAP_OFFSET + from + 2] += tax

            # check timestamp field to see if target account exists
            if contract.storage[ACCOUNT_MAP_OFFSET + to + 1] == 0:
                # register new account
                nr_accounts = contract.storage[ACCOUNT_LIST_OFFSET - 1]
                contract.storage[ACCOUNT_LIST_OFFSET + nr_accounts] = to
                contract.storage[ACCOUNT_LIST_OFFSET - 1] += 1
                contract.storage[ACCOUNT_MAP_OFFSET + to + 1] = block.timestamp

            return(1)
        else:
            return([0, "insufficient balance"], 2)

    elif msg.data[0] == "hash":
        proposal_id = sha3(msg.data[1])
        return(proposal_id)

    elif msg.data[0] == "propose":
        from = msg.sender
        # check if sender has an account and has tax credits
        if contract.storage[ACCOUNT_MAP_OFFSET + from + 2] == 0:
            return([0, "sender has no tax credits"], 2)

        proposal_id = sha3(msg.data[1])
        # check if proposal doesn't already exist
        if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id]:
            return([0, "proposal already exists"])

        to = msg.data[2]
        # check if recipient is a valid address and has an account (with timestamp)
        if to == 0 or to >= 2^160:
            return([0, "invalid address"], 2)
        if contract.storage[ACCOUNT_MAP_OFFSET + to + 1] == 0:
            return([0, "invalid to account"], 2)

        value = msg.data[3]
        # check if there is enough money in the commons account
        if value > contract.storage[COMMONS]:
            return([0, "not enough credits in commons"], 2)

        # record proposal in list
        nr_proposals = contract.storage[PROPOSAL_LIST_OFFSET - 1]
        contract.storage[PROPOSAL_LIST_OFFSET + nr_proposals] = proposal_id
        contract.storage[PROPOSAL_LIST_OFFSET - 1] += 1

        # record proposal in map
        contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = to
        contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] = value

        return(proposal_id)

    elif msg.data[0] == "vote":
        from = msg.sender
        proposal_id = sha3(msg.data[1])
        value = msg.data[2]
        # check if sender has an account and has tax credits
        if value < contract.storage[ACCOUNT_MAP_OFFSET + from + 2]:
            return([0, "sender doesn't have enough tax credits"], 2)

        # check if proposal exist
        if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] == 0:
            return([0, "proposal doesn't exist"], 2)

        # increase votes
        contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] += value
        # withdraw tax credits
        contract.storage[ACCOUNT_MAP_OFFSET + from + 2] -= value

        # did we reach 2/3 threshold?
        if contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] >= contract.storage[COMMONS] * 2 / 3:
            # got majority
            to = contract.storage[PROPOSAL_MAP_OFFSET + proposal_id]
            amount = contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1]

            # adjust balances
            contract.storage[ACCOUNT_MAP_OFFSET + to] += amount
            contract.storage[COMMONS] -= amount

            # reset proposal
            contract.storage[PROPOSAL_MAP_OFFSET + proposal_id] = 0
            contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 1] = 0
            contract.storage[PROPOSAL_MAP_OFFSET + proposal_id + 2] = 0
            return(1)

        return(proposal_id)

    elif msg.data[0] == "tick":
        nr_accounts = contract.storage[ACCOUNT_LIST_OFFSET - 1]
        account_idx = 0
        tax_paid = 0
        # process all accounts and see if they have to pay their daily capital tax
        while account_idx < nr_accounts:
            cur_account = contract.storage[ACCOUNT_LIST_OFFSET + account_idx]
            last_timestamp = contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 1]
            time_diff = block.timestamp - last_timestamp
            if time_diff >= 86400:
                tax_days = time_diff / 86400
                balance = contract.storage[ACCOUNT_MAP_OFFSET + cur_account]
                tax = tax_days * (balance / CAPITAL_TAX_PER_DAY)
                if tax > 0:
                    # charge capital tax, but give tax credits in return
                    contract.storage[ACCOUNT_MAP_OFFSET + cur_account] -= tax
                    contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 1] += tax_days * 86400
                    contract.storage[ACCOUNT_MAP_OFFSET + cur_account + 2] += tax

                    contract.storage[COMMONS] += tax
                    tax_paid += 1
            account_idx += 1
        return(tax_paid) # how many accounts did we charge tax on

    else:
        return([0, "unknown command"], 2)