Skip to content

Commit

Permalink
Add function balanceOf(...) (ethereum#5172)
Browse files Browse the repository at this point in the history
- ... as flat file reference implementation
  • Loading branch information
TimDaub authored and nachomazzara committed Jan 13, 2023
1 parent 617322f commit a94cd0a
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 311 deletions.
30 changes: 18 additions & 12 deletions EIPS/eip-4973.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,37 @@ pragma solidity ^0.8.6;
/// @title Account-bound tokens
/// @dev See https://eips.ethereum.org/EIPS/eip-4973
/// Note: the ERC-165 identifier for this interface is 0x6352211e.
/// Note: the ERC-165 identifier for this interface is 0x5164cf47.
interface IERC4973 /* is ERC165, ERC721Metadata */ {
/// @dev This emits when a new token is created and bound to an account by
/// any mechanism.
/// Note: For a reliable `_from` parameter, retrieve the transaction's
/// Note: For a reliable `from` parameter, retrieve the transaction's
/// authenticated `from` field.
event Attest(address indexed _to, uint256 indexed _tokenId);
event Attest(address indexed to, uint256 indexed tokenId);
/// @dev This emits when an existing ABT is revoked from an account and
/// destroyed by any mechanism.
/// Note: For a reliable `_from` parameter, retrieve the transaction's
/// Note: For a reliable `from` parameter, retrieve the transaction's
/// authenticated `from` field.
event Revoke(address indexed _to, uint256 indexed _tokenId);
event Revoke(address indexed to, uint256 indexed tokenId);
/// @notice Count all ABTs assigned to an owner
/// @dev ABTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param owner An address for whom to query the balance
/// @return The number of ABTs owned by `owner`, possibly zero
function balanceOf(address owner) external view returns (uint256);
/// @notice Find the address bound to an ERC4973 account-bound token
/// @dev ABTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an ABT
/// @param tokenId The identifier for an ABT
/// @return The address of the owner bound to the ABT
function ownerOf(uint256 _tokenId) external view returns (address);
function ownerOf(uint256 tokenId) external view returns (address);
/// @notice Destroys `tokenId`. At any time, an ABT receiver must be able to
/// disassociate themselves from an ABT publicly through calling this
/// function.
/// @dev Must emit a `event Revoke` with the `address _to` field pointing to
/// @dev Must emit a `event Revoke` with the `address to` field pointing to
/// the zero address.
/// @param _tokenId The identifier for an ABT
function burn(uint256 _tokenId) external;
/// @param tokenId The identifier for an ABT
function burn(uint256 tokenId) external;
}
```

Expand Down Expand Up @@ -112,11 +118,11 @@ ABTs can be indexed by tracking the emission of `event Attest` and `event Revoke

## Backwards Compatibility

We have adopted the [`EIP-165`](./eip-165.md) and `ERC721Metadata` functions purposefully to create a high degree of backward compatibility with [`EIP-721`](./eip-721.md). We have deliberately used [`EIP-721`](./eip-721.md) terminology such as `function ownerOf(...)` to minimize the effort of familiarization for `EIP-4973` implementers already familiar with, e.g., [`EIP-20`](./eip-20.md) or [`EIP-721`](./eip-721.md).
We have adopted the [`EIP-165`](./eip-165.md) and `ERC721Metadata` functions purposefully to create a high degree of backward compatibility with [`EIP-721`](./eip-721.md). We have deliberately used [`EIP-721`](./eip-721.md) terminology such as `function ownerOf(...)` or `function balanceOf(...)` to minimize the effort of familiarization for `EIP-4973` implementers already familiar with, e.g., [`EIP-20`](./eip-20.md) or [`EIP-721`](./eip-721.md).

## Reference Implementation

You can find an implementation of this standard in [../assets/eip-4973](../assets/eip-4973/src/ERC4973.sol).
You can find an implementation of this standard in [../assets/eip-4973](../assets/eip-4973/ERC-4973.sol).

## Copyright

Expand Down
174 changes: 174 additions & 0 deletions assets/eip-4973/ERC-4973.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.6;

// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}

interface IERC721Metadata {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}

/// @title Account-bound tokens
/// @dev See https://eips.ethereum.org/EIPS/eip-4973
/// Note: the ERC-165 identifier for this interface is 0x5164cf47.
interface IERC4973 /* is ERC165, ERC721Metadata */ {
/// @dev This emits when a new token is created and bound to an account by
/// any mechanism.
/// Note: For a reliable `from` parameter, retrieve the transaction's
/// authenticated `from` field.
event Attest(address indexed to, uint256 indexed tokenId);
/// @dev This emits when an existing ABT is revoked from an account and
/// destroyed by any mechanism.
/// Note: For a reliable `from` parameter, retrieve the transaction's
/// authenticated `from` field.
event Revoke(address indexed to, uint256 indexed tokenId);
/// @notice Count all ABTs assigned to an owner
/// @dev ABTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param owner An address for whom to query the balance
/// @return The number of ABTs owned by `owner`, possibly zero
function balanceOf(address owner) external view returns (uint256);
/// @notice Find the address bound to an ERC4973 account-bound token
/// @dev ABTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param tokenId The identifier for an ABT
/// @return The address of the owner bound to the ABT
function ownerOf(uint256 tokenId) external view returns (address);
/// @notice Destroys `tokenId`. At any time, an ABT receiver must be able to
/// disassociate themselves from an ABT publicly through calling this
/// function.
/// @dev Must emit a `event Revoke` with the `address to` field pointing to
/// the zero address.
/// @param tokenId The identifier for an ABT
function burn(uint256 tokenId) external;
}

/// @notice Reference implementation of EIP-4973 tokens.
/// @author TimDaub (https://github.com/rugpullindex/ERC4973/blob/master/src/ERC4973.sol)
abstract contract ERC4973 is ERC165, IERC721Metadata, IERC4973 {
string private _name;
string private _symbol;

mapping(uint256 => address) private _owners;
mapping(uint256 => string) private _tokenURIs;
mapping(address => uint256) private _balances;

constructor(
string memory name_,
string memory symbol_
) {
_name = name_;
_symbol = symbol_;
}

function supportsInterface(bytes4 interfaceId) public view override returns (bool) {
return
interfaceId == type(IERC721Metadata).interfaceId ||
interfaceId == type(IERC4973).interfaceId ||
super.supportsInterface(interfaceId);
}

function name() public view virtual override returns (string memory) {
return _name;
}

function symbol() public view virtual override returns (string memory) {
return _symbol;
}

function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "tokenURI: token doesn't exist");
return _tokenURIs[tokenId];
}

function burn(uint256 tokenId) public virtual override {
require(msg.sender == ownerOf(tokenId), "burn: sender must be owner");
_burn(tokenId);
}

function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "balanceOf: address zero is not a valid owner");
return _balances[owner];
}

function ownerOf(uint256 tokenId) public view virtual returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ownerOf: token doesn't exist");
return owner;
}

function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}

function _mint(
address to,
uint256 tokenId,
string memory uri
) internal virtual returns (uint256) {
require(!_exists(tokenId), "mint: tokenID exists");
_balances[to] += 1;
_owners[tokenId] = to;
_tokenURIs[tokenId] = uri;
emit Attest(to, tokenId);
return tokenId;
}

function _burn(uint256 tokenId) internal virtual {
address owner = ownerOf(tokenId);

_balances[owner] -= 1;
delete _owners[tokenId];
delete _tokenURIs[tokenId];

emit Revoke(owner, tokenId);
}
}

29 changes: 0 additions & 29 deletions assets/eip-4973/src/ERC165.sol

This file was deleted.

80 changes: 0 additions & 80 deletions assets/eip-4973/src/ERC4973.sol

This file was deleted.

Loading

0 comments on commit a94cd0a

Please sign in to comment.