/
FeeRegistar.sol
201 lines (161 loc) · 6.83 KB
/
FeeRegistar.sol
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! FeeRegistrar contract.
//! By Parity Technologies, 2017.
//! Released under the Apache Licence 2.
pragma solidity ^0.4.16;
// From Owned.sol
contract Owned {
/// STORAGE
address public owner = msg.sender;
/// EVENTS
event NewOwner(address indexed old, address indexed current);
/// MODIFIERS
modifier only_owner { require (msg.sender == owner); _; }
/// RESTRICTED PUBLIC METHODS
function setOwner(address _new) public only_owner { NewOwner(owner, _new); owner = _new; }
}
/// @title Delegated Contract
/// @notice This contract can be used to have a a system of delegates
/// who can be authorized to execute certain methods. A (super-)owner
/// is set, who can modify the delegates.
contract Delegated is Owned {
/// STORAGE
mapping (address => bool) delegates;
/// MODIFIERS
modifier only_delegate { require (msg.sender == owner || delegates[msg.sender]); _; }
/// PUBLIC METHODS
function delegate(address who) public constant returns (bool) { return who == owner || delegates[who]; }
/// RESTRICTED PUBLIC METHODS
function addDelegate(address _new) public only_owner { delegates[_new] = true; }
function removeDelegate(address _old) public only_owner { delete delegates[_old]; }
}
/// @title Fee Registrar
/// @author Nicolas Gotchac <[email protected]>
/// @notice This contract records fee payments. The address who deploys the contract
/// is set as the `owner` of the contract (which can be later modified). The `fee`
/// which users will have to pay must be specified, as well as the address of the treasury
/// to which the fee will be forwarded to.
/// A payment is a transaction with the value set as the `fee` value, and an address is
/// given as an argument. The given address will be marked as _paid for_, and the number
/// of times it was paid for will be recorded. We also record who is at the origin of the
/// payment.
/// For example, Alice can pay for Bob, and Eve can pay for Bob as well. This contract
/// will record that Bob is marked as paid, 2 times, by Alice and Eve.
/// A payment can be revoked by specified delegates, and the fund should be restored to
/// the payer of the fee.
contract FeeRegistrar is Delegated {
/// STORAGE
address public treasury;
uint public fee;
// a mapping of addresses to the origin of payments struct
mapping(address => address[]) s_paid;
/// EVENTS
event Paid (address who, address payer);
/// CONSTRUCTOR
/// @notice Contructor method of the contract, which
/// will set the `treasury` where payments will be send to,
/// and the `fee` users have to pay
/// @param _treasury The address to which the payments will be forwarded
/// @param _fee The fee users have to pay, in wei
function FeeRegistrar (address _treasury, uint _fee) public {
owner = msg.sender;
treasury = _treasury;
fee = _fee;
}
/// PUBLIC CONSTANT METHODS
/// @notice Returns for the given address the number of times
/// it was paid for, and an array of addresses who actually paid for the fee
/// (as one might pay the fee for another address)
/// @param who The address of the payer whose info we check
/// @return The count (number of payments) and the origins (the senders of the
/// payment)
function payer (address who) public constant returns (uint count, address[] origins) {
address[] memory m_origins = s_paid[who];
return (m_origins.length, m_origins);
}
/// @notice Returns whether the given address paid or not
/// @param who The address whose payment status we check
/// @ return Whether the address is marked as paid or not
function paid (address who) public constant returns (bool) {
return s_paid[who].length > 0;
}
/// PUBLIC METHODS
/// @notice This method is used to pay for the fee. You can pay
/// the fee for one address (then marked as paid), from another
/// address. The origin of the transaction, the
/// fee payer (`msg.sender`) is stored in an array.
/// The value of the transaction must
/// match the fee that was set in the contructor.
/// The only restriction is that you can't pay for the null
/// address.
/// You also can't pay more than 10 times for the same address
/// The value that is received is directly transfered to the
/// `treasury`.
/// @param who The address which should be marked as paid.
function pay (address who) external payable {
// We first check that the given address is not the null address
require(who != 0x0);
// Then check that the value matches with the fee
require(msg.value == fee);
// Maximum 10 payments per address
require(s_paid[who].length < 10);
s_paid[who].push(msg.sender);
// Send the paid event
Paid(who, msg.sender);
// Send the message value to the treasury
treasury.transfer(msg.value);
}
/// RESTRICTED (owner or delegate only) PUBLIC METHODS
/// @notice This method can only be called by the contract
/// owner, and can be used to virtually create a new payment,
/// by `origin` for `who`.
/// @param who The address that `origin` paid for
/// @param origin The virtual sender of the payment
function inject (address who, address origin) external only_owner {
// Add the origin address to the list of payers
s_paid[who].push(origin);
// Emit the `Paid` event
Paid(who, origin);
}
/// @notice This method can be called by authorized persons only,
/// and can issue a refund of the fee to the `origin` address who
/// paid the fee for `who`.
/// @param who The address that `origin` paid for
/// @param origin The sender of the payment, to which we shall
/// send the refund
function revoke (address who, address origin) payable external only_delegate {
// The value must match the current fee, so we can refund
// the payer, since the contract doesn't hold anything.
require(msg.value == fee);
bool found;
// Go through the list of payers to find
// the remove the right one
// NB : this list is limited to 10 items,
// @see the `pay` method
for (uint i = 0; i < s_paid[who].length; i++) {
if (s_paid[who][i] != origin) {
continue;
}
// If the origin payer is found
found = true;
uint last = s_paid[who].length - 1;
// Switch the last element of the array
// with the one to remove
s_paid[who][i] = s_paid[who][last];
// Remove the last element of the array
delete s_paid[who][last];
s_paid[who].length -= 1;
break;
}
// Ensure that the origin payer has been found
require(found);
// Refund the fee to the origin payer
who.transfer(msg.value);
}
/// @notice Change the address of the treasury, the address to which
/// the payments are forwarded to. Only the owner of the contract
/// can execute this method.
/// @param _treasury The new treasury address
function setTreasury (address _treasury) external only_owner {
treasury = _treasury;
}
}