-
Notifications
You must be signed in to change notification settings - Fork 15
/
SafeERC20Namer.sol
97 lines (84 loc) · 3.82 KB
/
SafeERC20Namer.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "openzeppelin/utils/Strings.sol";
// modified from https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/SafeERC20Namer.sol
// produces token descriptors from inconsistent or absent ERC20 symbol implementations that can return string or bytes32
// this library will always produce a string symbol to represent the token
library SafeERC20Namer {
function bytes32ToString(bytes32 x) private pure returns (string memory) {
bytes memory bytesString = new bytes(32);
uint256 charCount = 0;
for (uint256 j = 0; j < 32; j++) {
bytes1 char = x[j];
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (uint256 j = 0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
return string(bytesStringTrimmed);
}
// assumes the data is in position 2
function parseStringData(bytes memory b) private pure returns (string memory) {
uint256 charCount = 0;
// first parse the charCount out of the data
for (uint256 i = 32; i < 64; i++) {
charCount <<= 8;
charCount += uint8(b[i]);
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (uint256 i = 0; i < charCount; i++) {
bytesStringTrimmed[i] = b[i + 64];
}
return string(bytesStringTrimmed);
}
// uses a heuristic to produce a token name from the address
// the heuristic returns the full hex of the address string
function addressToName(address token) private pure returns (string memory) {
return Strings.toHexString(uint160(token));
}
// uses a heuristic to produce a token symbol from the address
// the heuristic returns the first 4 hex of the address string
function addressToSymbol(address token) private pure returns (string memory) {
return Strings.toHexString(uint160(token) >> (160 - 4 * 4));
}
// calls an external view token contract method that returns a symbol or name, and parses the output into a string
function callAndParseStringReturn(address token, bytes4 selector) private view returns (string memory) {
(bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(selector));
// if not implemented, or returns empty data, return empty string
if (!success || data.length == 0) {
return "";
}
// bytes32 data always has length 32
if (data.length == 32) {
bytes32 decoded = abi.decode(data, (bytes32));
return bytes32ToString(decoded);
} else if (data.length > 64) {
return abi.decode(data, (string));
}
return "";
}
// attempts to extract the token symbol. if it does not implement symbol, returns a symbol derived from the address
function tokenSymbol(address token) internal view returns (string memory) {
// 0x95d89b41 = bytes4(keccak256("symbol()"))
string memory symbol = callAndParseStringReturn(token, 0x95d89b41);
if (bytes(symbol).length == 0) {
// fallback to 6 uppercase hex of address
return addressToSymbol(token);
}
return symbol;
}
// attempts to extract the token name. if it does not implement name, returns a name derived from the address
function tokenName(address token) internal view returns (string memory) {
// 0x06fdde03 = bytes4(keccak256("name()"))
string memory name = callAndParseStringReturn(token, 0x06fdde03);
if (bytes(name).length == 0) {
// fallback to full hex of address
return addressToName(token);
}
return name;
}
}