-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcryptobank.sol
More file actions
324 lines (267 loc) · 10.2 KB
/
cryptobank.sol
File metadata and controls
324 lines (267 loc) · 10.2 KB
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
pragma solidity ^0.4.0;
contract cryptobank {
// Simplified version for Code Force Hackathon
/* All numbers in cents of currency units */
/* Common (global) public variables, types and constants */
// The address that controls the bank.
address public bank;
// The fee rate.
uint256 public feePerMillion;
// The maximum fee charged.
uint256 public maxFee;
// The name of the bank.
bytes32 public bankName;
// The BIC code of the bank.
bytes32 public bankCode;
// The currency that balances of this bank are expressed in.
bytes32 public currency;
// Flag to indicate the system is paused for maintenance (set by the bank)
bool public pausedForMaintenance;
// Currency tags:
bytes32 constant USD = "USD";
bytes32 constant EUR = "EUR";
bytes32 constant PLN = "PLN";
bytes32 constant GBP = "GBP";
bytes32 constant MXN = "MXN";
bytes32 constant BRL = "BRL";
bytes32 constant CLP = "CLP";
bytes32 constant CHF = "CHF";
bytes32 constant AUD = "AUD";
bytes32 constant NZD = "NZD";
bytes32 constant JPY = "JPY";
bytes32 constant CAD = "CAD";
// Internal struct representing an account in the bank.
struct Account {
// To check if this account is still active (we don't delete accounts).
bool active;
// The address that created and owns this account.
address owner;
// The accounts ballance. Can be negative.
int256 balance;
// The maximum allowed overdraft of the account.
uint256 overdraft;
// Whether the account is blocked.
bool blocked;
}
// An array of all the accounts.
Account[] public accounts;
// For efficiently finding the account of a certain address.
mapping(address => uint256) public accountByOwner;
/* Modifiers */
// Only the bank can perform this function.
modifier bankOnly {
if (msg.sender != bank)
throw;
_;
}
// Check if the given account number corresponds to an existing account.
modifier accountExists(uint256 account) {
if (account >= accounts.length)
throw;
_;
}
// Only the owner of the account with given account number can perform this
// function.
modifier senderOnly(uint256 account) {
if (msg.sender != accounts[account].owner)
throw;
_;
}
// Check if the account number corresponds to an unblocked account.
modifier notBlocked(uint256 account) {
if (accounts[account].blocked)
throw;
_;
}
// Check that the system is not paused for maintenance by the bank.
modifier notPaused() {
if(pausedForMaintenance)
throw;
_;
}
/* Constructor */
function cryptobank(bytes32 _bankCode, bytes32 _currency) {
bank = msg.sender;
accounts.push(Account(true, bank, 0, 0, false)); // This will be the bank's P&L account (#0)
feePerMillion = 1000;
maxFee = 100;
bankCode = _bankCode;
currency = _currency;
}
/* ERC20 token standard functions */
// Triggered when tokens are transferred.
event Transfer(address indexed from, address indexed to, uint256 value);
// Unsupported
//
// Triggered whenever approve(address spender, uint256 value) is called.
event Approval(address indexed owner, address indexed spender, uint256 value);
// Get the total token supply.
function totalSupply() constant returns (uint256 totalSupply) {
int256 totalAmount = 0;
for(uint256 i = 0; i < accounts.length; i++) {
totalAmount += accounts[i].balance;
}
return uint256(totalAmount);
}
// Get the account balance of another account with address owner.
function balanceOf(address owner) constant returns (uint256 balance) {
for(uint256 i = 0; i < accounts.length; i++) {
if(owner == accounts[i].owner) {
return uint256(accounts[i].balance);
}
}
return 0;
}
// Send value amount of tokens to address to.
function transfer(address to, uint256 value) returns (bool success) {
makeTransfer(uint256(getAccountNumber(msg.sender)), value, uint256(getAccountNumber(to)), "");
return true;
}
// Unsupported
//
// Send value amount of tokens from address from to address to.
function transferFrom(address from, address to, uint256 value) returns (bool success) {
return false;
}
// Unsupported
//
// Allow spender to withdraw from your account, multiple times, up to the
// value amount. If this function is called again it overwrites the current
// allowance with value.
function approve(address spender, uint256 value) returns (bool success) {
return false;
}
// Unsupported
//
// Returns the amount which spender is still allowed to withdraw from owner.
function allowance(address owner, address spender) constant returns (uint256 remaining) {
return 0;
}
/* User functions: */
// Anyone can open an account, which will be associated to a public address
function openAccount() notPaused returns (uint256 accountNumber) {
int256 acct = getAccountNumber(msg.sender);
if(acct >= 0)
return uint256(acct);
accounts.push(Account(true, msg.sender, 0, 0, false));
accountNumber = accounts.length - 1;
accountByOwner[msg.sender] = accountNumber;
return accountNumber;
}
// message field for reference purposes only - although it will not have any effect in the transaction
// itself, it will be stored in the blockchain and therefore will be available to be used as reference
// for subsequent actions
function makeTransfer(uint256 sender, uint256 amount, uint256 receiver, bytes32 message)
senderOnly(sender)
notBlocked(sender)
notBlocked(receiver)
accountExists(receiver)
notPaused
returns (bool success) {
uint256 fees = (feePerMillion * amount) / 1000000;
if(fees > maxFee) {
fees = maxFee;
}
if(accounts[sender].balance + int256(accounts[sender].overdraft) >= int256(amount)) {
accounts[sender].balance -= int256(amount);
accounts[receiver].balance += int256(amount - fees);
accounts[0].balance += int256(fees);
Transfer(accounts[sender].owner, accounts[receiver].owner, uint256(amount));
return true;
} else {
throw;
}
}
function redeemFunds(uint256 sender, uint256 funds, uint256 redemptionMode,
bytes32 routingInfo)
accountExists(sender)
senderOnly(sender)
notPaused {
if(accounts[sender].balance + int256(accounts[sender].overdraft) >= int256(funds)) {
accounts[sender].balance -= int256(funds);
accounts[0].balance += int256(funds);
} else {
throw;
}
}
// Different redemption modes to be used in redeemFunds.
uint256 constant REDEMPTION_MODE_UNKNOWN = 0;
uint256 constant REDEMPTION_MODE_REFER_TO_TRANSFER = 1;
uint256 constant REDEMPTION_MODE_ROUTE_TO_ACCOUNT = 2;
uint256 constant REDEMPTION_MODE_RETURN_PAYMENT = 3;
// Result codes for redemptions.
uint256 constant REDEMPTION_SUCCESS = 0;
uint256 constant REDEMPTION_USER_UNKNOWN_TO_BANK = 1;
uint256 constant REDEMPTION_BANK_ACCOUNT_NOT_FOUND = 2;
uint256 constant REDEMPTION_BANK_TRANSFER_FAILED = 3;
uint256 constant REDEMPTION_CASHOUT_LIMIT_EXCEEDED = 4;
uint256 constant REDEMPTION_PAYMENT_FAILED = 5;
uint256 constant REDEMPTION_UNKNOWN_REDEMPTION_MODE = 6;
uint256 constant REDEMPTION_FAILED_UNSPECIFIED = 7;
/* Backoffice functions: */
// Change the name of this bank.
function setBankName(bytes32 _bankName) bankOnly {
bankName = _bankName;
}
// Update the fee policy of this bank.
function setFees(uint256 _feePerMillion, uint256 _maxFee) bankOnly {
feePerMillion = _feePerMillion;
maxFee = _maxFee;
}
// Add new funds to the given account.
function addFunds(uint256 account, uint256 funds) bankOnly accountExists(account) {
accounts[account].balance += int256(funds);
}
// Remove funds from the given account.
function removeFunds(uint256 account, uint256 funds, uint256 redemptionHash, uint256 errorCode)
bankOnly accountExists(account) {
if(accounts[account].balance + int256(accounts[account].overdraft) >= int256(funds)) {
accounts[account].balance -= int256(funds);
} else {
throw;
}
}
// Change the overdraft allowance for the given account.
function setOverdraft(uint256 account, uint256 limit) bankOnly accountExists(account) {
accounts[account].overdraft = limit;
}
// Block the given account.
function blockAccount(uint256 account) bankOnly accountExists(account) {
accounts[account].blocked = true;
}
// Unblock the given account.
function unblockAccount(uint256 account) bankOnly accountExists(account) {
accounts[account].blocked = false;
}
function pause_for_maintenance() bankOnly {
pausedForMaintenance = true;
}
function resume() bankOnly {
pausedForMaintenance = false;
}
/* Public system info functions */
// Get the account number of the account associated with the given address.
function getAccountNumber(address user) constant returns (int256) {
uint256 nb = accountByOwner[user];
if (nb == 0) {
// account does not exist
return -1;
} else {
return int256(nb);
}
}
// Get the total number of accounts.
function numberOfAccounts() constant returns (uint256) {
return accounts.length;
}
/* Special functions */
// Close the bank.
function closeDown() bankOnly {
selfdestruct(bank);
}
// Change the address of the bank.
function changeBankAddress(address newAddress) bankOnly {
bank = newAddress;
}
function () { throw; }
}