-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathTokenConverter.sol
563 lines (449 loc) · 22.6 KB
/
TokenConverter.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
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.19;
import "https://github.com/Dexaran/ERC223-token-standard/blob/development/token/ERC223/IERC223Recipient.sol";
import "https://github.com/Dexaran/ERC223-token-standard/blob/development/utils/Address.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/ERC165.sol";
/**
* @dev Interface of the ERC223 standard token as defined in the EIP-223 https://eips.ethereum.org/EIPS/eip-223.
Using a custom IERC223 here as it redefines the "transfer" function as payable.
*/
abstract contract IERC223 {
function name() public view virtual returns (string memory);
function symbol() public view virtual returns (string memory);
function decimals() public view virtual returns (uint8);
function totalSupply() public view virtual returns (uint256);
/**
* @dev Returns the balance of the `who` address.
*/
function balanceOf(address who) public virtual view returns (uint);
/**
* @dev Transfers `value` tokens from `msg.sender` to `to` address
* and returns `true` on success.
*/
function transfer(address to, uint value) public virtual returns (bool success);
/**
* @dev Transfers `value` tokens from `msg.sender` to `to` address with `data` parameter
* and returns `true` on success.
*/
function transfer(address to, uint value, bytes calldata data) public payable virtual returns (bool success);
/**
* @dev Event that is fired on successful transfer.
*/
event Transfer(address indexed from, address indexed to, uint value, bytes data);
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
interface standardERC20
{
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC223WrapperToken {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function standard() external view returns (string memory);
function origin() external view returns (address);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external payable returns (bool);
function transfer(address to, uint256 value, bytes calldata data) external payable returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function mint(address _recipient, uint256 _quantity) external;
function burn(address _recipient, uint256 _quantity) external;
}
interface IERC20WrapperToken {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function standard() external view returns (string memory);
function origin() external view returns (address);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 value) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function mint(address _recipient, uint256 _quantity) external;
function burn(address _recipient, uint256 _quantity) external;
}
/**
ERC-223 Wrapper is a token that is created by the TokenConverter contract
and can be exchanged 1:1 for it's original ERC-20 version at any time
this version implements `approve` and `transferFrom` features for backwards compatibility reasons
even though we do not recommend using this pattern to transfer ERC-223 tokens.
*/
contract ERC223WrapperToken is IERC223, ERC165
{
address public creator = msg.sender;
address private wrapper_for;
mapping(address account => mapping(address spender => uint256)) private allowances;
event Transfer(address indexed from, address indexed to, uint256 amount);
event TransferData(bytes data);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*
constructor(address _wrapper_for)
{
wrapper_for = _wrapper_for;
}
*/
function set(address _wrapper_for) external
{
require(msg.sender == creator);
wrapper_for = _wrapper_for;
}
uint256 private _totalSupply;
mapping(address => uint256) private balances; // List of user balances.
function totalSupply() public view override returns (uint256) { return _totalSupply; }
function balanceOf(address _owner) public view override returns (uint256) { return balances[_owner]; }
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return
interfaceId == type(IERC20).interfaceId ||
interfaceId == type(standardERC20).interfaceId ||
interfaceId == type(IERC223WrapperToken).interfaceId ||
interfaceId == type(IERC223).interfaceId ||
super.supportsInterface(interfaceId);
}
function transfer(address _to, uint _value, bytes calldata _data) public payable override returns (bool success)
{
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
if(Address.isContract(_to)) {
IERC223Recipient(_to).tokenReceived(msg.sender, _value, _data);
}
if (msg.value > 0) payable(_to).transfer(msg.value);
emit Transfer(msg.sender, _to, _value, _data);
emit Transfer(msg.sender, _to, _value); // Old ERC-20 compatible event. Added for backwards compatibility reasons.
return true;
}
function transfer(address _to, uint _value) public override returns (bool success)
{
bytes memory _empty = hex"00000000";
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
if(Address.isContract(_to)) {
IERC223Recipient(_to).tokenReceived(msg.sender, _value, _empty);
}
emit Transfer(msg.sender, _to, _value, _empty);
emit Transfer(msg.sender, _to, _value); // Old ERC-20 compatible event. Added for backwards compatibility reasons.
return true;
}
function name() public view override returns (string memory) { return IERC20(wrapper_for).name(); }
function symbol() public view override returns (string memory) { return string.concat(IERC20(wrapper_for).name(), "223"); }
function decimals() public view override returns (uint8) { return IERC20(wrapper_for).decimals(); }
function standard() public view returns (string memory) { return "223"; }
function origin() public view returns (address) { return wrapper_for; }
function mint(address _recipient, uint256 _quantity) external
{
require(msg.sender == creator, "Wrapper Token: Only the creator contract can mint wrapper tokens.");
balances[_recipient] += _quantity;
_totalSupply += _quantity;
}
function burn(uint256 _quantity) external
{
require(msg.sender == creator, "Wrapper Token: Only the creator contract can destroy wrapper tokens.");
balances[msg.sender] -= _quantity;
_totalSupply -= _quantity;
}
// ERC-20 functions for backwards compatibility.
function allowance(address owner, address spender) public view virtual returns (uint256) {
return allowances[owner][spender];
}
function approve(address _spender, uint _value) public returns (bool) {
// Safety checks.
require(_spender != address(0), "ERC-223: Spender error.");
allowances[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(address _from, address _to, uint _value) public returns (bool) {
require(allowances[_from][msg.sender] >= _value, "ERC-223: Insufficient allowance.");
balances[_from] -= _value;
allowances[_from][msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(_from, _to, _value);
return true;
}
}
contract ERC20WrapperToken is IERC20, ERC165
{
address public creator = msg.sender;
address public wrapper_for;
mapping(address account => mapping(address spender => uint256)) private allowances;
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*
constructor(address _wrapper_for)
{
wrapper_for = _wrapper_for;
}
*/
function set(address _wrapper_for) external
{
require(msg.sender == creator);
wrapper_for = _wrapper_for;
}
uint256 private _totalSupply;
mapping(address => uint256) private balances; // List of user balances.
function balanceOf(address _owner) public view override returns (uint256) { return balances[_owner]; }
function name() public view override returns (string memory) { return IERC20(wrapper_for).name(); }
function symbol() public view override returns (string memory) { return string.concat(IERC223(wrapper_for).name(), "20"); }
function decimals() public view override returns (uint8) { return IERC20(wrapper_for).decimals(); }
function totalSupply() public view override returns (uint256) { return _totalSupply; }
function origin() public view returns (address) { return wrapper_for; }
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return
interfaceId == type(IERC20).interfaceId ||
interfaceId == type(IERC20WrapperToken).interfaceId ||
super.supportsInterface(interfaceId);
}
function transfer(address _to, uint _value) public override returns (bool success)
{
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function mint(address _recipient, uint256 _quantity) external
{
require(msg.sender == creator, "Wrapper Token: Only the creator contract can mint wrapper tokens.");
balances[_recipient] += _quantity;
_totalSupply += _quantity;
}
function burn(address _from, uint256 _quantity) external
{
require(msg.sender == creator, "Wrapper Token: Only the creator contract can destroy wrapper tokens.");
balances[_from] -= _quantity;
_totalSupply -= _quantity;
}
function allowance(address owner, address spender) public view virtual returns (uint256) {
return allowances[owner][spender];
}
function approve(address _spender, uint _value) public returns (bool) {
// Safety checks.
require(_spender != address(0), "ERC-20: Spender error.");
allowances[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(address _from, address _to, uint _value) public returns (bool) {
require(allowances[_from][msg.sender] >= _value, "ERC-20: Insufficient allowance.");
balances[_from] -= _value;
allowances[_from][msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(_from, _to, _value);
return true;
}
}
contract TokenStandardConverter is IERC223Recipient
{
address public ownerMultisig;
event ERC223WrapperCreated(address indexed _token, address indexed _ERC223Wrapper);
event ERC20WrapperCreated(address indexed _token, address indexed _ERC20Wrapper);
mapping (address => ERC223WrapperToken) public erc223Wrappers; // A list of token wrappers. First one is ERC-20 origin, second one is ERC-223 version.
mapping (address => ERC20WrapperToken) public erc20Wrappers;
mapping (address => address) public erc223Origins;
mapping (address => address) public erc20Origins;
mapping (address => uint256) public erc20Supply; // Token => how much was deposited.
function getERC20WrapperFor(address _token) public view returns (address, string memory)
{
if ( address(erc20Wrappers[_token]) != address(0) )
{
return (address(erc20Wrappers[_token]), "ERC-20");
}
return (address(0), "Error");
}
function getERC223WrapperFor(address _token) public view returns (address, string memory)
{
if ( address(erc223Wrappers[_token]) != address(0) )
{
return (address(erc223Wrappers[_token]), "ERC-223");
}
return (address(0), "Error");
}
function getERC20OriginFor(address _token) public view returns (address)
{
return (address(erc20Origins[_token]));
}
function getERC223OriginFor(address _token) public view returns (address)
{
return (address(erc223Origins[_token]));
}
function predictWrapperAddress(address _token, bool _isERC20) view external returns (address)
{
bytes memory _bytecode;
if(_isERC20)
{
_bytecode= type(ERC223WrapperToken).creationCode;
}
else
{
_bytecode= type(ERC20WrapperToken).creationCode;
}
bytes32 hash = keccak256(
abi.encodePacked(
bytes1(0xff), address(this), keccak256(abi.encode(_token)), keccak256(_bytecode)
)
);
return address(uint160(uint(hash)));
}
function tokenReceived(address _from, uint _value, bytes memory _data) public override returns (bytes4)
{
require(erc223Origins[msg.sender] == address(0), "Error: creating wrapper for a wrapper token.");
// There are two possible cases:
// 1. A user deposited ERC-223 origin token to convert it to ERC-20 wrapper
// 2. A user deposited ERC-223 wrapper token to unwrap it to ERC-20 origin.
if(erc20Origins[msg.sender] != address(0))
{
// Origin for deposited token exists.
// Unwrap ERC-223 wrapper.
safeTransfer(erc20Origins[msg.sender], _from, _value);
erc20Supply[erc20Origins[msg.sender]] -= _value;
//erc223Wrappers[msg.sender].burn(_value);
ERC223WrapperToken(msg.sender).burn(_value);
return this.tokenReceived.selector;
}
// Otherwise origin for the sender token doesn't exist
// There are two possible cases:
// 1. ERC-20 wrapper for the deposited token exists
// 2. ERC-20 wrapper for the deposited token doesn't exist and must be created.
else if(address(erc20Wrappers[msg.sender]) == address(0))
{
// Create ERC-20 wrapper if it doesn't exist.
createERC20Wrapper(msg.sender);
}
// Mint ERC-20 wrapper tokens for the deposited ERC-223 token
// if the ERC-20 wrapper didn't exist then it was just created in the above statement.
erc20Wrappers[msg.sender].mint(_from, _value);
return this.tokenReceived.selector;
}
function createERC223Wrapper(address _token) public returns (address)
{
require(address(erc223Wrappers[_token]) == address(0), "ERROR: Wrapper exists");
require(getERC20OriginFor(_token) == address(0), "ERROR: 20 wrapper creation");
require(getERC223OriginFor(_token) == address(0), "ERROR: 223 wrapper creation");
//ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken(_token);
ERC223WrapperToken _newERC223Wrapper = new ERC223WrapperToken{salt: keccak256(abi.encode(_token))}();
_newERC223Wrapper.set(_token);
erc223Wrappers[_token] = _newERC223Wrapper;
erc20Origins[address(_newERC223Wrapper)] = _token;
emit ERC223WrapperCreated(_token, address(_newERC223Wrapper));
return address(_newERC223Wrapper);
}
function createERC20Wrapper(address _token) public returns (address)
{
require(address(erc20Wrappers[_token]) == address(0), "ERROR: Wrapper already exists.");
require(getERC20OriginFor(_token) == address(0), "ERROR: 20 wrapper creation");
require(getERC223OriginFor(_token) == address(0), "ERROR: 223 wrapper creation");
ERC20WrapperToken _newERC20Wrapper = new ERC20WrapperToken{salt: keccak256(abi.encode(_token))}();
_newERC20Wrapper.set(_token);
erc20Wrappers[_token] = _newERC20Wrapper;
erc223Origins[address(_newERC20Wrapper)] = _token;
emit ERC20WrapperCreated(_token, address(_newERC20Wrapper));
return address(_newERC20Wrapper);
}
function depositERC20(address _token, uint256 _amount) public returns (bool)
{
if(erc223Origins[_token] != address(0))
{
return unwrapERC20toERC223(_token, _amount);
}
else return wrapERC20toERC223(_token, _amount);
}
function wrapERC20toERC223(address _ERC20token, uint256 _amount) public returns (bool)
{
// If there is no active wrapper for a token that user wants to wrap
// then create it.
if(address(erc223Wrappers[_ERC20token]) == address(0))
{
createERC223Wrapper(_ERC20token);
}
uint256 _converterBalance = IERC20(_ERC20token).balanceOf(address(this)); // Safety variable.
//IERC20(_ERC20token).transferFrom(msg.sender, address(this), _amount);
safeTransferFrom(_ERC20token, msg.sender, address(this), _amount);
_amount = IERC20(_ERC20token).balanceOf(address(this)) - _converterBalance;
erc20Supply[_ERC20token] += _amount;
erc223Wrappers[_ERC20token].mint(msg.sender, _amount);
return true;
}
function unwrapERC20toERC223(address _ERC20token, uint256 _amount) public returns (bool)
{
require(IERC20(_ERC20token).balanceOf(msg.sender) >= _amount, "Error: Insufficient balance.");
require(erc223Origins[_ERC20token] != address(0), "Error: provided token is not a ERC-20 wrapper.");
ERC20WrapperToken(_ERC20token).burn(msg.sender, _amount);
safeTransfer(erc223Origins[_ERC20token], msg.sender, _amount);
return true;
}
function convertERC20(address _token, uint256 _amount) public returns (bool)
{
if(isWrapper(_token)) return unwrapERC20toERC223(_token, _amount);
else return wrapERC20toERC223(_token, _amount);
}
function isWrapper(address _token) public view returns (bool)
{
return erc20Origins[_token] != address(0) || erc223Origins[_token] != address(0);
}
/*
function convertERC223toERC20(address _from, uint256 _amount) public returns (bool)
{
// If there is no active wrapper for a token that user wants to wrap
// then create it.
if(address(erc20Wrappers[msg.sender]) == address(0))
{
createERC223Wrapper(msg.sender);
}
erc20Wrappers[msg.sender].mint(_from, _amount);
return true;
}
*/
function rescueERC20(address _token) external {
require(msg.sender == ownerMultisig, "ERROR: Only owner can do this.");
require(address(erc20Wrappers[_token]) == address(0), "Withdrawing ERC-223 origin is not allowed.");
uint256 _stuckTokens = IERC20(_token).balanceOf(address(this)) - erc20Supply[_token];
//IERC20(_token).transfer(msg.sender, _stuckTokens);
safeTransfer(_token, msg.sender, _stuckTokens);
}
function transferOwnership(address _newOwner) public
{
require(msg.sender == ownerMultisig, "ERROR: Only owner can call this function.");
ownerMultisig = _newOwner;
}
// ************************************************************
// Functions that address problems with tokens that pretend to be ERC-20
// but in fact are not compatible with the ERC-20 standard transferring methods.
// EIP20 https://eips.ethereum.org/EIPS/eip-20
// ************************************************************
function safeTransfer(address token, address to, uint value) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
}
function safeTransferFrom(address token, address from, address to, uint value) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
}
}