如何检测以太坊地址是否为 ERC20 代币合约?
How to detect if an Ethereum address is an ERC20 token contract?
如果我只从输入中得到一个以太坊地址,有没有办法确定它是否符合 ERC20 令牌标准?
有很多方法可以实现这一点。一种可能的快速但肮脏的解决方案是通过调用以下命令检查合约地址上是否存在 ERC20 函数:
eth.call({to:contractAddress, data:web3.sha3("balanceOf(address)")})
非 ERC20 将 return 'null' 0x
十六进制响应,而 ERC20 将为您提供 32 字节 uint
,在本例中为 0,但如果您提供数据中的一个地址,然后它将为您提供该地址的实际代币余额。
这不是确定合同是否为 ERC20 的保证方式,因为其他合同可能公开相同的功能,但这是一种快速简便的检查。您可以在 totalSupply()
等上添加额外的电话以进行更多确认。
ERC165 tackles this problem, but, unfortunately, most ERC20 implementations don't support it (as of Nov 2018, at least OpenZeppelin doesn't). This means that you could try calling the supportsInterface 函数,但无论如何它都会恢复,你宁愿把事情复杂化。
尽管如此,ERC721 中是这样定义的:
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
虽然不能保证所有实现都定义接口 id,但考虑到社区同意从一开始就应用 ERC165 这一事实,它在 ERC721 的情况下工作的可能性更高。如果下面查询的 return 值为真,则意味着你有一个合规合约,否则只需恢复交易。
// you can call this in your contracts
IERC721(contractAddress).supportsInterface(0x80ac58cd)
此外,手动检查给定方法的 bytes4
的有用资源是 4byte.directory
如果你问的是链下,那么使用这些函数:
getContract(url, smartContractAddress){
const Web3Eth = require('web3-eth');
const abi_ = this.getABI();
const web3Eth = new Web3Eth(Web3Eth.givenProvider || url);
return new web3Eth.Contract(abi_, smartContractAddress);
}
async getERCtype(contract){
const is721 = await contract.methods.supportsInterface('0x80ac58cd').call();
if(is721){
return "ERC721";
}
const is1155 = await contract.methods.supportsInterface('0xd9b67a26').call();
if(is1155){
return "ERC1155";
}
return undefined;
}
getABI(){
return [
{"constant":true,"inputs": [
{"internalType":"bytes4","name": "","type": "bytes4"}],
"name": "supportsInterface",
"outputs": [{"internalType":"bool","name": "","type": "bool"}],
"payable": false,"stateMutability":"view","type": "function"}
];
}
像这样:
const contract = getContract(url, smartContractAddress);
const type = await getERCtype(contract);
console.log(type);
如果我只从输入中得到一个以太坊地址,有没有办法确定它是否符合 ERC20 令牌标准?
有很多方法可以实现这一点。一种可能的快速但肮脏的解决方案是通过调用以下命令检查合约地址上是否存在 ERC20 函数:
eth.call({to:contractAddress, data:web3.sha3("balanceOf(address)")})
非 ERC20 将 return 'null' 0x
十六进制响应,而 ERC20 将为您提供 32 字节 uint
,在本例中为 0,但如果您提供数据中的一个地址,然后它将为您提供该地址的实际代币余额。
这不是确定合同是否为 ERC20 的保证方式,因为其他合同可能公开相同的功能,但这是一种快速简便的检查。您可以在 totalSupply()
等上添加额外的电话以进行更多确认。
ERC165 tackles this problem, but, unfortunately, most ERC20 implementations don't support it (as of Nov 2018, at least OpenZeppelin doesn't). This means that you could try calling the supportsInterface 函数,但无论如何它都会恢复,你宁愿把事情复杂化。
尽管如此,ERC721 中是这样定义的:
bytes4 private constant _InterfaceId_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
虽然不能保证所有实现都定义接口 id,但考虑到社区同意从一开始就应用 ERC165 这一事实,它在 ERC721 的情况下工作的可能性更高。如果下面查询的 return 值为真,则意味着你有一个合规合约,否则只需恢复交易。
// you can call this in your contracts
IERC721(contractAddress).supportsInterface(0x80ac58cd)
此外,手动检查给定方法的 bytes4
的有用资源是 4byte.directory
如果你问的是链下,那么使用这些函数:
getContract(url, smartContractAddress){
const Web3Eth = require('web3-eth');
const abi_ = this.getABI();
const web3Eth = new Web3Eth(Web3Eth.givenProvider || url);
return new web3Eth.Contract(abi_, smartContractAddress);
}
async getERCtype(contract){
const is721 = await contract.methods.supportsInterface('0x80ac58cd').call();
if(is721){
return "ERC721";
}
const is1155 = await contract.methods.supportsInterface('0xd9b67a26').call();
if(is1155){
return "ERC1155";
}
return undefined;
}
getABI(){
return [
{"constant":true,"inputs": [
{"internalType":"bytes4","name": "","type": "bytes4"}],
"name": "supportsInterface",
"outputs": [{"internalType":"bool","name": "","type": "bool"}],
"payable": false,"stateMutability":"view","type": "function"}
];
}
像这样:
const contract = getContract(url, smartContractAddress);
const type = await getERCtype(contract);
console.log(type);