-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathTeamJust.sol
497 lines (444 loc) · 23.3 KB
/
TeamJust.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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
pragma solidity ^0.4.24;
/* -Team Just- v0.2.5
* ┌┬┐┌─┐┌─┐┌┬┐ ╦╦ ╦╔═╗╔╦╗ ┌─┐┬─┐┌─┐┌─┐┌─┐┌┐┌┌┬┐┌─┐
* │ ├┤ ├─┤│││ ║║ ║╚═╗ ║ ├─┘├┬┘├┤ └─┐├┤ │││ │ └─┐
* ┴ └─┘┴ ┴┴ ┴ ╚╝╚═╝╚═╝ ╩ ┴ ┴└─└─┘└─┘└─┘┘└┘ ┴ └─┘
* _____ _____
* (, / /) /) /) (, / /) /)
* ┌─┐ / _ (/_ // // / _ // _ __ _(/
* ├─┤ ___/___(/_/(__(_/_(/_(/_ ___/__/_)_(/_(_(_/ (_(_(_
* ┴ ┴ / / .-/ _____ (__ /
* (__ / (_/ (, / /)™
* / __ __ __ __ _ __ __ _ _/_ _ _(/
* ┌─┐┬─┐┌─┐┌┬┐┬ ┬┌─┐┌┬┐ /__/ (_(__(_)/ (_/_)_(_)/ (_(_(_(__(/_(_(_
* ├─┘├┬┘│ │ │││ ││ │ (__ / .-/ © Jekyll Island Inc. 2018
* ┴ ┴└─└─┘─┴┘└─┘└─┘ ┴ (_/
* JJJJJJJJJJUUUUUUUU UUUUUUUU SSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTT
*==============J:::::::::U::::::U=====U::::::USS:::::::::::::::T:::::::::::::::::::::T======*
* J:::::::::U::::::U U::::::S:::::SSSSSS::::::T:::::::::::::::::::::T
* JJ:::::::JUU:::::U U:::::US:::::S SSSSSST:::::TT:::::::TT:::::T
* J:::::J U:::::U U:::::US:::::S TTTTTT T:::::T TTTTTT
* J::_________ : ________::::US::_::S ____ ____ T:::::T
* J:| _ _ |:|_ __ |:::U S/ \:SSSS |_ \ / _| T:::::T
* J:|_/:| |U\_|::D| |_ \_|:::U / _ \::::SSS| \/ | T:::::T
* J:::::| |U:::::D| _| _::::U/ ___ \::::::| |\ /| | T:::::T
* JJJJJJJ J::::_| |_:::::_| |__/ |::_/ / \ \_SS _| |_\/_| |_ T:::::T
* J:::::J J:::|_____|:::|________|:|____| |____| |_____||_____| T:::::T
* J::::::J J::::::J U::::::U U::::::U S:::::S T:::::T
* J:::::::JJJ:::::::J U:::::::UUU:::::::USSSSSSS S:::::S TT:::::::TT
* JJ:::::::::::::JJ UU:::::::::::::UU S::::::SSSSSS:::::S T:::::::::T
*=======JJ:::::::::JJ========UU:::::::::UU===S:::::::::::::::SS======T:::::::::T============*
* JJJJJJJJJ UUUUUUUUU SSSSSSSSSSSSSSS TTTTTTTTTTT
*
* ╔═╗┌─┐┌┐┌┌┬┐┬─┐┌─┐┌─┐┌┬┐ ╔═╗┌─┐┌┬┐┌─┐ ┌──────────┐
* ║ │ ││││ │ ├┬┘├─┤│ │ ║ │ │ ││├┤ │ Inventor │
* ╚═╝└─┘┘└┘ ┴ ┴└─┴ ┴└─┘ ┴ ╚═╝└─┘─┴┘└─┘ └──────────┘
*
* ┌──────────────────────────────────────────────────────────────────────┐
* │ Que up intensely spectacular intro music... In walks, Team Just. │
* │ Everyone goes crazy. │
* │ This is a companion to MSFun. It's a central database of Devs and │
* │ Admin's that we can import to any dapp to allow them management │
* │ permissions. │
* └──────────────────────────────────────────────────────────────────────┘
* ┌────────────────────┐
* │ Setup Instructions │
* └────────────────────┘
* (Step 1) import this contracts interface into your contract
*
* import "./TeamJustInterface.sol";
*
* (Step 2) set up the interface to point to the TeamJust contract
*
* TeamJustInterface constant TeamJust = TeamJustInterface(0x464904238b5CdBdCE12722A7E6014EC1C0B66928);
*
* modifier onlyAdmins() {require(TeamJust.isAdmin(msg.sender) == true, "onlyAdmins failed - msg.sender is not an admin"); _;}
* modifier onlyDevs() {require(TeamJust.isDev(msg.sender) == true, "onlyDevs failed - msg.sender is not a dev"); _;}
* ┌────────────────────┐
* │ Usage Instructions │
* └────────────────────┘
* use onlyAdmins() and onlyDevs() modifiers as you normally would on any function
* you wish to restrict to admins/devs registered with this contract.
*
* to get required signatures for admins or devs
* uint256 x = TeamJust.requiredSignatures();
* uint256 x = TeamJust.requiredDevSignatures();
*
* to get admin count or dev count
* uint256 x = TeamJust.adminCount();
* uint256 x = TeamJust.devCount();
*
* to get the name of an admin (in bytes32 format)
* bytes32 x = TeamJust.adminName(address);
*/
interface JIincForwarderInterface {
function deposit() external payable returns(bool);
function status() external view returns(address, address, bool);
function startMigration(address _newCorpBank) external returns(bool);
function cancelMigration() external returns(bool);
function finishMigration() external returns(bool);
function setup(address _firstCorpBank) external;
}
contract TeamJust {
JIincForwarderInterface private Jekyll_Island_Inc = JIincForwarderInterface(0x0);
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// SET UP MSFun (note, check signers by name is modified from MSFun sdk)
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MSFun.Data private msData;
function deleteAnyProposal(bytes32 _whatFunction) onlyDevs() public {MSFun.deleteProposal(msData, _whatFunction);}
function checkData(bytes32 _whatFunction) onlyAdmins() public view returns(bytes32 message_data, uint256 signature_count) {return(MSFun.checkMsgData(msData, _whatFunction), MSFun.checkCount(msData, _whatFunction));}
function checkSignersByName(bytes32 _whatFunction, uint256 _signerA, uint256 _signerB, uint256 _signerC) onlyAdmins() public view returns(bytes32, bytes32, bytes32) {return(this.adminName(MSFun.checkSigner(msData, _whatFunction, _signerA)), this.adminName(MSFun.checkSigner(msData, _whatFunction, _signerB)), this.adminName(MSFun.checkSigner(msData, _whatFunction, _signerC)));}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// DATA SETUP
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
struct Admin {
bool isAdmin;
bool isDev;
bytes32 name;
}
mapping (address => Admin) admins_;
uint256 adminCount_;
uint256 devCount_;
uint256 requiredSignatures_;
uint256 requiredDevSignatures_;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// CONSTRUCTOR
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
constructor()
public
{
address inventor = 0x18E90Fc6F70344f53EBd4f6070bf6Aa23e2D748C;
address mantso = 0x8b4DA1827932D71759687f925D17F81Fc94e3A9D;
address justo = 0x8e0d985f3Ec1857BEc39B76aAabDEa6B31B67d53;
address sumpunk = 0x7ac74Fcc1a71b106F12c55ee8F802C9F672Ce40C;
address deployer = 0xF39e044e1AB204460e06E87c6dca2c6319fC69E3;
admins_[inventor] = Admin(true, true, "inventor");
admins_[mantso] = Admin(true, true, "mantso");
admins_[justo] = Admin(true, true, "justo");
admins_[sumpunk] = Admin(true, true, "sumpunk");
admins_[deployer] = Admin(true, true, "deployer");
adminCount_ = 5;
devCount_ = 5;
requiredSignatures_ = 1;
requiredDevSignatures_ = 1;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// FALLBACK, SETUP, AND FORWARD
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// there should never be a balance in this contract. but if someone
// does stupidly send eth here for some reason. we can forward it
// to jekyll island
function ()
public
payable
{
Jekyll_Island_Inc.deposit.value(address(this).balance)();
}
function setup(address _addr)
onlyDevs()
public
{
require( address(Jekyll_Island_Inc) == address(0) );
Jekyll_Island_Inc = JIincForwarderInterface(_addr);
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// MODIFIERS
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
modifier onlyDevs()
{
require(admins_[msg.sender].isDev == true, "onlyDevs failed - msg.sender is not a dev");
_;
}
modifier onlyAdmins()
{
require(admins_[msg.sender].isAdmin == true, "onlyAdmins failed - msg.sender is not an admin");
_;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// DEV ONLY FUNCTIONS
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/**
* @dev DEV - use this to add admins. this is a dev only function.
* @param _who - address of the admin you wish to add
* @param _name - admins name
* @param _isDev - is this admin also a dev?
*/
function addAdmin(address _who, bytes32 _name, bool _isDev)
public
onlyDevs()
{
if (MSFun.multiSig(msData, requiredDevSignatures_, "addAdmin") == true)
{
MSFun.deleteProposal(msData, "addAdmin");
// must check this so we dont mess up admin count by adding someone
// who is already an admin
if (admins_[_who].isAdmin == false)
{
// set admins flag to true in admin mapping
admins_[_who].isAdmin = true;
// adjust admin count and required signatures
adminCount_ += 1;
requiredSignatures_ += 1;
}
// are we setting them as a dev?
// by putting this outside the above if statement, we can upgrade existing
// admins to devs.
if (_isDev == true)
{
// bestow the honored dev status
admins_[_who].isDev = _isDev;
// increase dev count and required dev signatures
devCount_ += 1;
requiredDevSignatures_ += 1;
}
}
// by putting this outside the above multisig, we can allow easy name changes
// without having to bother with multisig. this will still create a proposal though
// so use the deleteAnyProposal to delete it if you want to
admins_[_who].name = _name;
}
/**
* @dev DEV - use this to remove admins. this is a dev only function.
* -requirements: never less than 1 admin
* never less than 1 dev
* never less admins than required signatures
* never less devs than required dev signatures
* @param _who - address of the admin you wish to remove
*/
function removeAdmin(address _who)
public
onlyDevs()
{
// we can put our requires outside the multisig, this will prevent
// creating a proposal that would never pass checks anyway.
require(adminCount_ > 1, "removeAdmin failed - cannot have less than 2 admins");
require(adminCount_ >= requiredSignatures_, "removeAdmin failed - cannot have less admins than number of required signatures");
if (admins_[_who].isDev == true)
{
require(devCount_ > 1, "removeAdmin failed - cannot have less than 2 devs");
require(devCount_ >= requiredDevSignatures_, "removeAdmin failed - cannot have less devs than number of required dev signatures");
}
// checks passed
if (MSFun.multiSig(msData, requiredDevSignatures_, "removeAdmin") == true)
{
MSFun.deleteProposal(msData, "removeAdmin");
// must check this so we dont mess up admin count by removing someone
// who wasnt an admin to start with
if (admins_[_who].isAdmin == true) {
//set admins flag to false in admin mapping
admins_[_who].isAdmin = false;
//adjust admin count and required signatures
adminCount_ -= 1;
if (requiredSignatures_ > 1)
{
requiredSignatures_ -= 1;
}
}
// were they also a dev?
if (admins_[_who].isDev == true) {
//set dev flag to false
admins_[_who].isDev = false;
//adjust dev count and required dev signatures
devCount_ -= 1;
if (requiredDevSignatures_ > 1)
{
requiredDevSignatures_ -= 1;
}
}
}
}
/**
* @dev DEV - change the number of required signatures. must be between
* 1 and the number of admins. this is a dev only function
* @param _howMany - desired number of required signatures
*/
function changeRequiredSignatures(uint256 _howMany)
public
onlyDevs()
{
// make sure its between 1 and number of admins
require(_howMany > 0 && _howMany <= adminCount_, "changeRequiredSignatures failed - must be between 1 and number of admins");
if (MSFun.multiSig(msData, requiredDevSignatures_, "changeRequiredSignatures") == true)
{
MSFun.deleteProposal(msData, "changeRequiredSignatures");
// store new setting.
requiredSignatures_ = _howMany;
}
}
/**
* @dev DEV - change the number of required dev signatures. must be between
* 1 and the number of devs. this is a dev only function
* @param _howMany - desired number of required dev signatures
*/
function changeRequiredDevSignatures(uint256 _howMany)
public
onlyDevs()
{
// make sure its between 1 and number of admins
require(_howMany > 0 && _howMany <= devCount_, "changeRequiredDevSignatures failed - must be between 1 and number of devs");
if (MSFun.multiSig(msData, requiredDevSignatures_, "changeRequiredDevSignatures") == true)
{
MSFun.deleteProposal(msData, "changeRequiredDevSignatures");
// store new setting.
requiredDevSignatures_ = _howMany;
}
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// EXTERNAL FUNCTIONS
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
function requiredSignatures() external view returns(uint256) {return(requiredSignatures_);}
function requiredDevSignatures() external view returns(uint256) {return(requiredDevSignatures_);}
function adminCount() external view returns(uint256) {return(adminCount_);}
function devCount() external view returns(uint256) {return(devCount_);}
function adminName(address _who) external view returns(bytes32) {return(admins_[_who].name);}
function isAdmin(address _who) external view returns(bool) {return(admins_[_who].isAdmin);}
function isDev(address _who) external view returns(bool) {return(admins_[_who].isDev);}
}
library MSFun {
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// DATA SETS
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// contact data setup
struct Data
{
mapping (bytes32 => ProposalData) proposal_;
}
struct ProposalData
{
// a hash of msg.data
bytes32 msgData;
// number of signers
uint256 count;
// tracking of wither admins have signed
mapping (address => bool) admin;
// list of admins who have signed
mapping (uint256 => address) log;
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// MULTI SIG FUNCTIONS
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
function multiSig(Data storage self, uint256 _requiredSignatures, bytes32 _whatFunction)
internal
returns(bool)
{
// our proposal key will be a hash of our function name + our contracts address
// by adding our contracts address to this, we prevent anyone trying to circumvent
// the proposal's security via external calls.
bytes32 _whatProposal = whatProposal(_whatFunction);
// this is just done to make the code more readable. grabs the signature count
uint256 _currentCount = self.proposal_[_whatProposal].count;
// store the address of the person sending the function call. we use msg.sender
// here as a layer of security. in case someone imports our contract and tries to
// circumvent function arguments. still though, our contract that imports this
// library and calls multisig, needs to use onlyAdmin modifiers or anyone who
// calls the function will be a signer.
address _whichAdmin = msg.sender;
// prepare our msg data. by storing this we are able to verify that all admins
// are approving the same argument input to be executed for the function. we hash
// it and store in bytes32 so its size is known and comparable
bytes32 _msgData = keccak256(msg.data);
// check to see if this is a new execution of this proposal or not
if (_currentCount == 0)
{
// if it is, lets record the original signers data
self.proposal_[_whatProposal].msgData = _msgData;
// record original senders signature
self.proposal_[_whatProposal].admin[_whichAdmin] = true;
// update log (used to delete records later, and easy way to view signers)
// also useful if the calling function wants to give something to a
// specific signer.
self.proposal_[_whatProposal].log[_currentCount] = _whichAdmin;
// track number of signatures
self.proposal_[_whatProposal].count += 1;
// if we now have enough signatures to execute the function, lets
// return a bool of true. we put this here in case the required signatures
// is set to 1.
if (self.proposal_[_whatProposal].count == _requiredSignatures) {
return(true);
}
// if its not the first execution, lets make sure the msgData matches
} else if (self.proposal_[_whatProposal].msgData == _msgData) {
// msgData is a match
// make sure admin hasnt already signed
if (self.proposal_[_whatProposal].admin[_whichAdmin] == false)
{
// record their signature
self.proposal_[_whatProposal].admin[_whichAdmin] = true;
// update log (used to delete records later, and easy way to view signers)
self.proposal_[_whatProposal].log[_currentCount] = _whichAdmin;
// track number of signatures
self.proposal_[_whatProposal].count += 1;
}
// if we now have enough signatures to execute the function, lets
// return a bool of true.
// we put this here for a few reasons. (1) in normal operation, if
// that last recorded signature got us to our required signatures. we
// need to return bool of true. (2) if we have a situation where the
// required number of signatures was adjusted to at or lower than our current
// signature count, by putting this here, an admin who has already signed,
// can call the function again to make it return a true bool. but only if
// they submit the correct msg data
if (self.proposal_[_whatProposal].count == _requiredSignatures) {
return(true);
}
}
}
// deletes proposal signature data after successfully executing a multiSig function
function deleteProposal(Data storage self, bytes32 _whatFunction)
internal
{
//done for readability sake
bytes32 _whatProposal = whatProposal(_whatFunction);
address _whichAdmin;
//delete the admins votes & log. i know for loops are terrible. but we have to do this
//for our data stored in mappings. simply deleting the proposal itself wouldn't accomplish this.
for (uint256 i=0; i < self.proposal_[_whatProposal].count; i++) {
_whichAdmin = self.proposal_[_whatProposal].log[i];
delete self.proposal_[_whatProposal].admin[_whichAdmin];
delete self.proposal_[_whatProposal].log[i];
}
//delete the rest of the data in the record
delete self.proposal_[_whatProposal];
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// HELPER FUNCTIONS
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
function whatProposal(bytes32 _whatFunction)
private
view
returns(bytes32)
{
return(keccak256(abi.encodePacked(_whatFunction,this)));
}
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// VANITY FUNCTIONS
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// returns a hashed version of msg.data sent by original signer for any given function
function checkMsgData (Data storage self, bytes32 _whatFunction)
internal
view
returns (bytes32 msg_data)
{
bytes32 _whatProposal = whatProposal(_whatFunction);
return (self.proposal_[_whatProposal].msgData);
}
// returns number of signers for any given function
function checkCount (Data storage self, bytes32 _whatFunction)
internal
view
returns (uint256 signature_count)
{
bytes32 _whatProposal = whatProposal(_whatFunction);
return (self.proposal_[_whatProposal].count);
}
// returns address of an admin who signed for any given function
function checkSigner (Data storage self, bytes32 _whatFunction, uint256 _signer)
internal
view
returns (address signer)
{
require(_signer > 0, "MSFun checkSigner failed - 0 not allowed");
bytes32 _whatProposal = whatProposal(_whatFunction);
return (self.proposal_[_whatProposal].log[_signer - 1]);
}
}