Sleep Minting
Abstract
A scam technique makes NFT mint & transfer look like originated from a famous account, and lures users to buy the scam NFTs in the collection.
Mechanism
Most dApps or websites are using Event logs of Mint & Transfer solely when analysing its origin, sender, receiver and etc..
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
The front end just listens to this event emitted from blockchain, and displays to users that some NFT was transferred from/to some addresses according to this event log.
The following is how transferFrom()
works:
Check if the caller is the owner of the NFT to be transferred
In the end, it will emit a
Transfer
event.
function transferFrom(address _from, address _to, uint256 _tokenId) external override {
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from, NOT_OWNER);
require(_to != address(0), ZERO_ADDRESS);
_transfer(_to, _tokenId);
}
function _transfer(address _to, uint256 _tokenId) internal virtual {
address from = idToOwner[_tokenId];
_clearApproval(_tokenId);
_removeNFToken(from, _tokenId);
_addNFToken(_to, _tokenId);
emit Transfer(from, _to, _tokenId);
}
Now we add a private address in the NFT721 contract,
address private ADMIN = "0x630664594134b641D78c9D2823385d8BAC63d4fF";
Then we modifiy the requirement in transferFrom()
and grant a super power to the ADMIN.
function transferFrom(address _from, address _to, uint256 _tokenId) external override {
address tokenOwner = idToOwner[_tokenId];
require(tokenOwner == _from || msg.sender == ADMIN, NOT_OWNER);
//...
_transfer(_to, _tokenId);
}
function _transfer(address _to, uint256 _tokenId) internal virtual {
address from = idToOwner[_tokenId];
//...
emit Transfer(from, _to, _tokenId);
}
ADMIN has the ability to transfer anyone's NFT while the Transfer
Event remains the same, who still report the ordinary from/to address. Thus, the front end will also resolve the result as usual, but this time it's wrong.
Countermeasure
For front end, it's easy to filter a Sleep Minting action:
// PESUDO CODE
if (transferEvent.from != tx.origin && Approval[_tokenId] != tx.origin){
"Sleep minted";
}
Though this may have side effects. You should modify the condition according to your use case.
References
Last updated