Skip to content

Commit 26d84aa

Browse files
committed
ERC1155: Add metadata contract
1 parent 85f0978 commit 26d84aa

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# Author: Sören Steiger, github.com/ssteiger
2+
# License: MIT
3+
4+
# ERC1155 Token Standard
5+
# https://eips.ethereum.org/EIPS/eip-1155
6+
7+
8+
contract ERC1155TokenReceiver:
9+
def onERC1155Received(
10+
_operator: address,
11+
_from: address,
12+
_id: uint256,
13+
_value: uint256,
14+
_data: bytes[256]
15+
) -> bytes32: modifying # TODO: should return bytes4
16+
def onERC1155BatchReceived(
17+
_operator: address,
18+
_from: address,
19+
_ids: uint256[BATCH_SIZE],
20+
_values: uint256[BATCH_SIZE],
21+
_data: bytes[256]
22+
) -> bytes32: modifying # TODO: should return bytes4
23+
24+
25+
# @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
26+
# The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
27+
# The `_from` argument MUST be the address of the holder whose balance is decreased.
28+
# The `_to` argument MUST be the address of the recipient whose balance is increased.
29+
# The `_id` argument MUST be the token type being transferred.
30+
# The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
31+
# When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
32+
# When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
33+
# event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value);
34+
TransferSingle: event({
35+
_operator: indexed(address),
36+
_from: indexed(address),
37+
_to: indexed(address),
38+
_id: uint256,
39+
_value: uint256
40+
})
41+
42+
# @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
43+
# The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
44+
# The `_from` argument MUST be the address of the holder whose balance is decreased.
45+
# The `_to` argument MUST be the address of the recipient whose balance is increased.
46+
# The `_ids` argument MUST be the list of tokens being transferred.
47+
# The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
48+
# When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
49+
# When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
50+
# event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values);
51+
TransferBatch: event({
52+
_operator: indexed(address),
53+
_from: indexed(address),
54+
_to: indexed(address),
55+
_ids: uint256[BATCH_SIZE],
56+
_value: uint256[BATCH_SIZE]
57+
})
58+
59+
# @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled).
60+
# event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
61+
ApprovalForAll: event({
62+
_owner: indexed(address),
63+
_operator: indexed(address),
64+
_approved: bool
65+
})
66+
67+
68+
69+
supportedInterfaces: map(bytes32, bool)
70+
71+
# https://eips.ethereum.org/EIPS/eip-165
72+
ERC165_INTERFACE_ID: constant(bytes32) = 0x0000000000000000000000000000000000000000000000000000000001ffc9a7
73+
ERC1155_INTERFACE_ID: constant(bytes32) = 0x00000000000000000000000000000000000000000000000000000000d9b67a26
74+
75+
tokensIdCount: uint256
76+
77+
_balanceOf: map(address, map(uint256, uint256))
78+
79+
operators: map(address, map(address, bool))
80+
81+
# TODO: decide which batch size to use
82+
BATCH_SIZE: constant(uint256) = 5
83+
84+
85+
86+
@public
87+
def __init__():
88+
self.tokensIdCount = 0
89+
self.supportedInterfaces[ERC165_INTERFACE_ID] = True
90+
self.supportedInterfaces[ERC1155_INTERFACE_ID] = True
91+
92+
93+
@public
94+
@constant
95+
def supportsInterface(_interfaceID: bytes32) -> bool:
96+
return self.supportedInterfaces[_interfaceID]
97+
98+
99+
# @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
100+
# @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
101+
# MUST revert if `_to` is the zero address.
102+
# MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
103+
# MUST revert on any other error.
104+
# MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
105+
# After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
106+
# @param _from Source address
107+
# @param _to Target address
108+
# @param _id ID of the token type
109+
# @param _value Transfer amount
110+
# @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
111+
# function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
112+
@public
113+
def safeTransferFrom(
114+
_from: address,
115+
_to: address,
116+
_id: uint256,
117+
_value: uint256,
118+
_data: bytes[256]
119+
):
120+
assert _to != ZERO_ADDRESS
121+
assert self._balanceOf[_from][_id] >= _value
122+
123+
if _to.is_contract:
124+
returnValue: bytes32 = ERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _id, _value, _data)
125+
assert returnValue == method_id("onERC1155Received(address,address,uint256,uint256,bytes)", bytes32)
126+
127+
self._balanceOf[_from][_id] -= _value
128+
self._balanceOf[_to][_id] += _value
129+
log.TransferSingle(msg.sender, _from, _to, _id, _value)
130+
131+
132+
# @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
133+
# @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
134+
# MUST revert if `_to` is the zero address.
135+
# MUST revert if length of `_ids` is not the same as length of `_values`.
136+
# MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
137+
# MUST revert on any other error.
138+
# MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
139+
# Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
140+
# After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
141+
# @param _from Source address
142+
# @param _to Target address
143+
# @param _ids IDs of each token type (order and length must match _values array)
144+
# @param _values Transfer amounts per token type (order and length must match _ids array)
145+
# @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
146+
# function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;
147+
@public
148+
def safeBatchTransferFrom(
149+
_from: address,
150+
_to: address,
151+
_ids: uint256[BATCH_SIZE],
152+
_values: uint256[BATCH_SIZE],
153+
_data: bytes[256]
154+
):
155+
assert _to != ZERO_ADDRESS
156+
#assert len(_ids) == len(_values)
157+
158+
for i in range(BATCH_SIZE):
159+
assert self._balanceOf[_from][_ids[i]] >= _values[i]
160+
self._balanceOf[_from][_ids[i]] -= _values[i]
161+
self._balanceOf[_to][_ids[i]] += _values[i]
162+
if _to.is_contract:
163+
returnValue: bytes32 = ERC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _ids[i], _values[i], _data)
164+
assert returnValue == method_id("onERC1155Received(address,address,uint256,uint256,bytes)", bytes32)
165+
166+
log.TransferBatch(msg.sender, _from, _to, _ids, _values)
167+
168+
169+
# @notice Get the balance of an account's tokens.
170+
# @param _owner The address of the token holder
171+
# @param _id ID of the token
172+
# @return The _owner's balance of the token type requested
173+
# function balanceOf(address _owner, uint256 _id) external view returns (uint256);
174+
@public
175+
@constant
176+
def balanceOf(
177+
_owner: address,
178+
_id: uint256
179+
) -> uint256:
180+
assert _owner != ZERO_ADDRESS
181+
return self._balanceOf[_owner][_id]
182+
183+
184+
# @notice Get the balance of multiple account/token pairs
185+
# @param _owners The addresses of the token holders
186+
# @param _ids ID of the tokens
187+
# @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
188+
# function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
189+
@public
190+
@constant
191+
def balanceOfBatch(
192+
_owner: address[BATCH_SIZE],
193+
_ids: uint256[BATCH_SIZE]
194+
) -> uint256[BATCH_SIZE]:
195+
returnValues: uint256[BATCH_SIZE]
196+
for i in range(BATCH_SIZE):
197+
returnValues[i] = self._balanceOf[_owner[i]][_ids[i]]
198+
return returnValues
199+
200+
201+
# @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
202+
# @dev MUST emit the ApprovalForAll event on success.
203+
# @param _operator Address to add to the set of authorized operators
204+
# @param _approved True if the operator is approved, false to revoke approval
205+
# function setApprovalForAll(address _operator, bool _approved) external;
206+
@public
207+
def setApprovalForAll(
208+
_operator: address,
209+
_approved: bool
210+
):
211+
(self.operators[msg.sender])[_operator] = _approved
212+
log.ApprovalForAll(msg.sender, _operator, _approved)
213+
214+
215+
# @notice Queries the approval status of an operator for a given owner.
216+
# @param _owner The owner of the tokens
217+
# @param _operator Address of authorized operator
218+
# @return True if the operator is approved, false if not
219+
# function isApprovedForAll(address _owner, address _operator) external view returns (bool);
220+
@public
221+
@constant
222+
def isApprovedForAll(
223+
_owner: address,
224+
_operator: address
225+
) -> bool:
226+
return (self.operators[_owner])[_operator]
227+
228+
229+
# NOTE: This is not part of the standard
230+
# TODO: Right now everyone can mint
231+
@public
232+
def mint(
233+
_to: address,
234+
_supply: uint256,
235+
_data: bytes[256]=""
236+
) -> uint256:
237+
self._balanceOf[msg.sender][self.tokensIdCount] = _supply
238+
self.tokensIdCount += 1
239+
log.TransferSingle(msg.sender, ZERO_ADDRESS, _to, self.tokensIdCount, _supply)
240+
return self.tokensIdCount
241+
242+
243+
# NOTE: This is not part of the standard
244+
# TODO: Right now everyone can mint
245+
@public
246+
def mintBatch(
247+
_to: address,
248+
_supplys: uint256[BATCH_SIZE],
249+
_data: bytes[256]=""
250+
) -> uint256[BATCH_SIZE]:
251+
assert _to != ZERO_ADDRESS
252+
ids: uint256[BATCH_SIZE]
253+
for i in range(BATCH_SIZE):
254+
self._balanceOf[msg.sender][self.tokensIdCount] = _supplys[i]
255+
self.tokensIdCount += 1
256+
id: uint256 = self.tokensIdCount
257+
ids[i] = id
258+
259+
log.TransferBatch(msg.sender, ZERO_ADDRESS, _to, ids, _supplys)
260+
return ids
261+
262+
263+
# TODO: specify a burn()/burnBatch() function

0 commit comments

Comments
 (0)