Skip to content

Instantly share code, notes, and snippets.

@z0r0z
Created February 28, 2022 08:29
Show Gist options
  • Save z0r0z/5c4d786c491e52eabde00ee47933e37d to your computer and use it in GitHub Desktop.
Save z0r0z/5c4d786c491e52eabde00ee47933e37d to your computer and use it in GitHub Desktop.
what ERC-721 could have been
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient NFT implementation adhering as closely to ERC20 interface.
/// @author Ross, modified from (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
abstract contract NFT {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
/*///////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public constant decimals = 0;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*///////////////////////////////////////////////////////////////
NFT STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(uint256 => address) public ownerOf;
mapping(uint256 => address) public getApproved;
/*///////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*///////////////////////////////////////////////////////////////
NFT LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual returns (bool) {
address owner = ownerOf[id];
require(msg.sender == owner, "NOT_OWNER");
getApproved[id] = spender;
emit Approval(owner, spender, id);
return true;
}
function transfer(address to, uint256 id) public virtual returns (bool) {
address owner = ownerOf[id];
require(msg.sender == owner, "NOT_OWNER");
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
balanceOf[owner]--;
balanceOf[to]++;
}
delete getApproved[id];
ownerOf[id] = to;
emit Transfer(owner, to, id);
return true;
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual returns (bool) {
require(from == ownerOf[id], "WRONG_FROM");
require(msg.sender == from || msg.sender == getApproved[id], "NOT_AUTHORIZED");
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
balanceOf[from]--;
balanceOf[to]++;
}
ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
return true;
}
/*///////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
balanceOf[to]++;
}
ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = ownerOf[id];
require(ownerOf[id] != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
balanceOf[owner]--;
}
delete ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment