Created
October 4, 2023 15:21
-
-
Save bergeron/8599eb33545b60bb32035df6fbeaf86e to your computer and use it in GitHub Desktop.
Smart contract to get token standard and details
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.18; | |
import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; | |
import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; | |
import "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol"; | |
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | |
contract GetTokenStandardAndDetails { | |
bytes4 private constant ERC721_INTERFACE = 0x80ac58cd; | |
bytes4 private constant ERC1155_INTERFACE = 0xd9b67a26; | |
string private constant ERC721 = "ERC721"; | |
string private constant ERC1155 = "ERC1155"; | |
string private constant ERC20 = "ERC20"; | |
struct StandardAndDetails { | |
string standard; | |
string tokenURI; | |
string symbol; | |
string name; | |
uint8 decimals; | |
uint256 balance; | |
} | |
// Returns the contract standard and details for a token address | |
function getTokenStandardAndDetails(address tokenAddress) public view returns (StandardAndDetails memory) { | |
if (tokenAddress.code.length == 0 ) { revert("Address is an EOA, not a contract"); } | |
StandardAndDetails memory details = StandardAndDetails({ | |
standard: "", symbol: "", name: "", tokenURI: "", decimals: 0, balance: 0 | |
}); | |
if (ERC165Checker.supportsInterface(tokenAddress, ERC721_INTERFACE)) { | |
details.standard = ERC721; | |
// The metadata extension is OPTIONAL for ERC721, so use try/catch | |
IERC721Metadata token = IERC721Metadata(tokenAddress); | |
try token.name() returns (string memory n) { details.name = n; } catch { } | |
try token.symbol() returns (string memory s) { details.symbol = s; } catch { } | |
} else if (ERC165Checker.supportsInterface(tokenAddress, ERC1155_INTERFACE)) { | |
details.standard = ERC1155; | |
// name() and symbol() aren't defined in ERC1155, so use try/catch | |
IERC1155WithNameAndSymbol token = IERC1155WithNameAndSymbol(tokenAddress); | |
try token.name() returns (string memory n) { details.name = n; } catch { } | |
try token.symbol() returns (string memory s) { details.symbol = s; } catch { } | |
} else { | |
IERC20Metadata token = IERC20Metadata(tokenAddress); | |
// ERC20 doesn't require ERC165 supportsInterface, so we'll determine based on | |
// symbol() and decimals() being defined, even though they're optional in ERC20. | |
try token.symbol() returns (string memory s) { details.symbol = s; } | |
catch { revert("Unable to determine contract standard"); } | |
try token.decimals() returns (uint8 d) { details.decimals = d; } | |
catch { revert("Unable to determine contract standard"); } | |
try token.name() returns (string memory n) { details.name = n; } | |
catch { } | |
details.standard = ERC20; | |
} | |
return details; | |
} | |
// Returns the contract standard and details for a token address and token id | |
function getTokenStandardAndDetails(address tokenAddress, uint256 tokenId) external view returns (StandardAndDetails memory) { | |
StandardAndDetails memory details = getTokenStandardAndDetails(tokenAddress); | |
if (equal(details.standard, ERC721)) { | |
// The metadata extension is OPTIONAL for ERC721 | |
IERC721Metadata token = IERC721Metadata(tokenAddress); | |
try token.tokenURI(tokenId) returns (string memory u) { details.tokenURI = u; } | |
catch { } | |
} else if (equal(details.standard, ERC1155)) { | |
// The metadata extension is OPTIONAL for ERC1155 | |
IERC1155MetadataURI token = IERC1155MetadataURI(tokenAddress); | |
try token.uri(tokenId) returns (string memory u) { details.tokenURI = u; } | |
catch { } | |
} | |
return details; | |
} | |
// Returns the contract standard and details for a token address and user address | |
function getTokenStandardAndDetails(address tokenAddress, address userAddress) external view returns (StandardAndDetails memory) { | |
StandardAndDetails memory details = getTokenStandardAndDetails(tokenAddress); | |
if (equal(details.standard, ERC20)) { | |
details.balance = IERC20Metadata(tokenAddress).balanceOf(userAddress); | |
} | |
return details; | |
} | |
function equal(string memory a, string memory b) internal pure returns (bool) { | |
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); | |
} | |
} | |
// name() and symbol() aren't defined in ERC1155 but sometimes they're implemented anyway | |
interface IERC1155WithNameAndSymbol is IERC1155MetadataURI { | |
function name() external view returns (string memory); | |
function symbol() external view returns (string memory); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment