-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathTokenClient.sol
174 lines (154 loc) · 7.98 KB
/
TokenClient.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./TokenAbstraction.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/*
@title Token Client
@author Daniel Gonzalez Abalde
@notice Abstracts some functionality of any token standard into a single instance.
@dev Reference an instance in your contract or inherit from this contract, and
make calls to this client to operate with tokens. It is Ownable to have call permissions.
*/
contract TokenClient is Ownable
{
bytes32[] internal _standards;
mapping(bytes32=>address) internal _abstractions;
address[] internal _allowedCallers;
mapping(address=>bool) internal _callerAllowed;
event StandardSupported(bytes32 indexed standard);
event StandardReplaced(bytes32 indexed standard, address indexed previous);
event StandardUnsupported(bytes32 indexed standard);
event TokenTransfered(Token indexed token, address indexed from, address indexed to);
event TokenSetTransfered(TokenSet indexed tokenSet, address indexed from, address indexed to);
// ################################## MODIFIERS ##################################
modifier standardSupported(bytes32 standard){
require(_supportsStandard(standard), "TokenClient: standard not supported");
_;
}
modifier onlyAllowed(){
if(!_isAllowedCaller(msg.sender)){
revert("TokenClient: caller not allowed");
}
_;
}
modifier validToken(Token calldata token){
require(token.Standard != bytes32(0), "TokenClient: token with empty standard");
require(token.Contract != address(0), "TokenClient: token with empty address");
require(token.Amount > 0, "TokenClient: token with zero amount");
_;
}
// ################################## EXTERNAL ##################################
function isStandard(bytes32 standard, address contractAddress) onlyAllowed() standardSupported(standard) external view returns(bool) {
return TokenAbstraction(_abstractions[standard]).isStandard(contractAddress);
}
function isOwner(Token calldata token, address account) onlyAllowed() standardSupported(token.Standard) external view returns (bool) {
return TokenAbstraction(_abstractions[token.Standard]).isOwner(token, account);
}
function isApproved(Token calldata token, address account, address operator) onlyAllowed() standardSupported(token.Standard) external view virtual returns (bool){
return TokenAbstraction(_abstractions[token.Standard]).isApproved(token, account, operator);
}
function balanceOf(Token calldata token, address account) onlyAllowed() standardSupported(token.Standard) external view virtual returns (uint256){
return TokenAbstraction(_abstractions[token.Standard]).balanceOf(token, account);
}
function transfer(Token calldata token, address from, address to) onlyAllowed() standardSupported(token.Standard) external payable virtual returns (bool success){
bytes memory resultData = _delegatecall(token.Standard, abi.encodeWithSelector(bytes4(0x8c5b2f8e), token, from, to));
success = abi.decode(resultData, (bool));
if(success){
emit TokenTransfered(token, from, to);
}
}
function isOwnerSet(TokenSet calldata tokenSet, address account) onlyAllowed() standardSupported(tokenSet.Standard) external view returns (bool) {
return TokenAbstraction(_abstractions[tokenSet.Standard]).isOwnerSet(tokenSet, account);
}
function isApprovedSet(TokenSet calldata tokenSet, address account, address operator) onlyAllowed() standardSupported(tokenSet.Standard) external view virtual returns (bool){
return TokenAbstraction(_abstractions[tokenSet.Standard]).isApprovedSet(tokenSet, account, operator);
}
function balanceOfSet(TokenSet calldata tokenSet, address account) onlyAllowed() standardSupported(tokenSet.Standard) external view virtual returns (uint256[] memory){
return TokenAbstraction(_abstractions[tokenSet.Standard]).balanceOfSet(tokenSet, account);
}
function transferSet(TokenSet calldata tokenSet, address from, address to) onlyAllowed() standardSupported(tokenSet.Standard) external payable virtual returns (bool success){
bytes memory resultData = _delegatecall(tokenSet.Standard, abi.encodeWithSelector(bytes4(0xd4144fc8), tokenSet, from, to));
success = abi.decode(resultData, (bool));
if(success){
emit TokenSetTransfered(tokenSet, from, to);
}
}
function getAbstraction(bytes32 standard) onlyAllowed() external view returns(address){ return _abstractions[standard]; }
function supportedStandards() onlyAllowed() external view returns(bytes32[] memory){ return _standards; }
function supportsStandard(bytes32 standard) onlyAllowed() external view returns(bool){ return _supportsStandard(standard); }
// ################################## ONLY OWNER ##################################
function support(address tokenAbstraction) onlyOwner() external {
require(tokenAbstraction != address(0), "TokenClient: zero address");
TokenAbstraction ta = TokenAbstraction(tokenAbstraction);
bytes32 standard = ta.standard();
require(standard != bytes32(0), "TokenClient: empty standard");
if(_supportsStandard(standard)){
address previous = address(_abstractions[standard]);
require(previous != tokenAbstraction, "TokenClient: tokenAbstraction already supported");
_abstractions[standard] = tokenAbstraction;
emit StandardReplaced(standard, previous);
}else{
_standards.push(standard);
_abstractions[standard] = tokenAbstraction;
emit StandardSupported(standard);
}
}
function unsupport(bytes32 standard) standardSupported(standard) external {
for(uint256 i=0; i<_standards.length; i++){
if(_standards[i] == standard){
if(_standards.length > 1){
_standards[i] = _standards[_standards.length-1];
}
_standards.pop();
break;
}
}
delete _abstractions[standard];
emit StandardUnsupported(standard);
}
function allowCaller(address to, bool approve) onlyOwner() external {
if(approve){
if(!_callerAllowed[to]){
_callerAllowed[to] = true;
_allowedCallers.push(to);
}
}else{
if(_callerAllowed[to]){
for(uint256 i=0; i<_allowedCallers.length; i++){
if(_allowedCallers[i] == to){
if(_allowedCallers.length > 1){
_allowedCallers[i] = _allowedCallers[_allowedCallers.length-1];
}
_allowedCallers.pop();
break;
}
}
delete _callerAllowed[to];
}
}
}
function getAllowedCallers() onlyOwner() external view returns(address[] memory){ return _allowedCallers; }
// ################################## INTERNAL ##################################
function _isAllowedCaller(address account) internal view virtual returns(bool){
if(account == owner()){
return true;
}else if(_allowedCallers.length > 0){
return _callerAllowed[account];
}else{
return true;
}
}
function _supportsStandard(bytes32 standard) internal view virtual returns(bool){
return _abstractions[standard] != address(0);
}
function _delegatecall(bytes32 standard, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = _abstractions[standard].delegatecall(data);
if (!success) {
if (returndata.length == 0) revert();
assembly {
revert(add(32, returndata), mload(returndata))
}
}
return returndata;
}
}