Skip to content

Commit 954ebe6

Browse files
committed
Nonfungible ERC1155 implementation
1 parent 94697be commit 954ebe6

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {ERC1155} from "../ERC1155.sol";
6+
import {Arrays} from "../../../utils/Arrays.sol";
7+
8+
/**
9+
* @dev Indicates that the operator is trying to mint a token that already exists.
10+
* @param id Identifier of token being minted
11+
* @param owner Current token owner
12+
*/
13+
error ERC1155NonfungibleDuplicate(uint256 id, address owner);
14+
15+
/**
16+
* @dev Indicates that the operator is trying mint/transfer/burn an amount > 1 of a token
17+
*/
18+
error ERC1155NonfungibleInvalidAmount(uint256 id, uint256 amount);
19+
20+
/**
21+
* @dev For tokens that are nonfungible but prefer to use {ERC1155} rather than {ERC721}.
22+
*
23+
* {ERC1155Nonfungible} takes advantage of nonfungibility constraint to replace data model
24+
* tracking multiple account balances per token with with a more gas-efficient data model
25+
* tracking unique ownership per token.
26+
*
27+
* Moreover {ERC1155Nonfungible} makes it possible to query the owner of a specific token via {ERC1155Nonfungible-ownerOf},
28+
* similar to {ERC721-ownerOf}, but differs from {ERC721-ownerOf} in that querying the owner of an inexistent token
29+
* will not revert but will return `address(0)`.
30+
*/
31+
abstract contract ERC1155Nonfungible is ERC1155 {
32+
using Arrays for uint256[];
33+
using Arrays for address[];
34+
35+
mapping(uint256 id => address) private _owners;
36+
37+
/**
38+
* @dev Returns the owner of the token `id`. Does NOT revert if token doesn't exist.
39+
*
40+
* {ERC1155Nonfungible-ownerOf} and {ERC1155Nonfungible-_setOwnerOf} may be overridden in tandem,
41+
* e.g. to store more token information along with the owner
42+
*/
43+
function ownerOf(uint256 id) public view virtual returns (address) {
44+
return _owners[id];
45+
}
46+
47+
/**
48+
* @dev {ERC1155Nonfungible-ownerOf} and {ERC1155Nonfungible-_setOwnerOf} may be overridden in tandem,
49+
* e.g. to store more token information along with the owner
50+
*/
51+
function _setOwnerOf(uint256 id, address owner) internal virtual {
52+
_owners[id] = owner;
53+
}
54+
55+
/**
56+
* @dev Replaces {ERC1155-balanceOf} implementation entirely.
57+
*/
58+
function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
59+
return account != address(0) && ownerOf(id) == account ? 1 : 0;
60+
}
61+
62+
/**
63+
* @dev Replaces {ERC1155-_update} implementation entirely.
64+
*/
65+
function _update(
66+
address from,
67+
address to,
68+
uint256[] memory ids,
69+
uint256[] memory values
70+
) internal virtual override {
71+
if (ids.length != values.length) {
72+
revert ERC1155InvalidArrayLength(ids.length, values.length);
73+
}
74+
75+
address operator = _msgSender();
76+
77+
for (uint256 i = 0; i < ids.length; ++i) {
78+
uint256 id = ids.unsafeMemoryAccess(i);
79+
uint256 value = values.unsafeMemoryAccess(i);
80+
81+
if (value == 1) {
82+
address currOwner = ownerOf(id);
83+
// Could be written more compactly, but clearer if mint tackled separately from transfer/burn
84+
if (from == address(0)) {
85+
if (currOwner == address(0)) {
86+
_setOwnerOf(id, to);
87+
} else {
88+
revert ERC1155NonfungibleDuplicate(id, currOwner);
89+
}
90+
} else {
91+
if (from == currOwner) {
92+
_setOwnerOf(id, to);
93+
} else {
94+
revert ERC1155InsufficientBalance({sender: from, balance: 0, needed: 1, tokenId: id});
95+
}
96+
}
97+
} else if (value == 0) {
98+
// ERC-1155 allows zero-value transfers
99+
} else {
100+
revert ERC1155NonfungibleInvalidAmount(id, value);
101+
}
102+
}
103+
104+
if (ids.length == 1) {
105+
uint256 id = ids.unsafeMemoryAccess(0);
106+
uint256 value = values.unsafeMemoryAccess(0);
107+
emit TransferSingle(operator, from, to, id, value);
108+
} else {
109+
emit TransferBatch(operator, from, to, ids, values);
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)