lip | title | author | discussions-to | status | type | created | requires |
---|---|---|---|---|---|---|---|
8 |
Identifiable Digital Asset |
Claudio Weck <claudio@fanzone.media>, Fabian Vogelsteller <fabian@lukso.network>, Matthew Stevens <@mattgstevens>, Ankit Kumar <@ankitkumar9018> |
https://discord.gg/E2rJPP4 (LUKSO), https://discord.gg/PQvJQtCV (FANZONE) |
Review |
LSP |
2021-09-02 |
ERC165, ERC725Y, LSP1, LSP2, LSP4, LSP17 |
The LSP8 Identifiable Digital Asset Standard defines a standard interface for uniquely identifiable digital assets. It allows tokens to be uniquely traded and given with metadata using ERC725Y and LSP4.
This standard defines a digital asset standard that can represent non-fungible tokens (NFTs).
Key functionalities of this asset standard include:
-
Flexible Asset Representation: The tokenId defined in the standard is
bytes32
allowing different tokenId identification including numbers, contract addresses, and any other unique identifiers (e.g: serial numbers, NFTs with unique names, hash values, etc...). -
Dynamic Information Attachment: Leverages ERC725Y to add generic information to the asset and to each tokenId even post-deployment according to the LSP4-DigitalAssetMetadata standard.
-
Secure Transfers: By checking whether the recipient is capable of handling the asset before the actual transfer, it avoids loss and transfer of tokens to uncontrolled addresses.
-
Transfer Interaction: Notifies the operator, sender, and the recipient about the transfer, allowing users to be informed about the incoming asset and decide how to react accordingly (e.g., denying the token, forwarding it, etc.).
-
Future-Proof Functionalities: Through [LSP17-ContractExtension], allows the asset to be extended and support new standardized functions and interface IDs over time.
-
Asset Flexibility and Discoverability: Offers several flexible features, such as batch transfers and the ability to add several operators, as well as the ability to discover them.
The motivation for developing this new digital asset standard can be organized into several key points, each showing a specific limitation of current token standards:
-
Limited Asset Representation: Current NFT standards represent tokenId as a simple
uint256
type with no indication about how to parse it, making the NFT very limited. -
Limited Asset Metadata: Current token standards offer limited metadata attachment capabilities, typically confined to basic elements like
name
,symbol
, andtokenURI
. This limitation is particularly restrictive for assets that require verifiable, on-chain metadata – such as details about creators, the community behind the token, or dynamic attributes that allow an NFT to evolve. -
No Interaction and Notification: Traditional token standards lack in terms of interaction, particularly in notifying recipients about transfers. As a result, users cannot be unaware of incoming tokens and lose the opportunity to respond, for example, react to transfers, whether to deny, accept, or forward the tokens.
-
Limited Functionalities: Many tokens are confined to the functionalities they possess at deployment, making them rigid and unable to adapt to new requirements or standards.
-
Risk of asset loss: A common issue with current assets is the risk of loss due to transfers to incorrect or uncontrolled addresses as these standards does not check whether the recipient is able to handle the asset or not.
-
Limited Features: The limitation of having only one operator and the absence of batch transfer capabilities or even the discoverability of tokens a user own restricts and provide bad user experience. Users need to rely on centralized indexers to know which tokens they own, need to do several transactions to do a batch of transfers, and cannot have more than one operator.
ERC165 interface id: 0x3a271706
The LSP8 interface ID is calculated as the XOR of the LSP8 interface (see interface cheat-sheet below) and the LSP17 Extendable interface ID.
function totalSupply() external view returns (uint256);
Returns the number of existing tokens.
Returns: uint256
the number of existing tokens.
function balanceOf(address tokenOwner) external view returns (uint256);
Returns the number of tokens owned by tokenOwner
.
Parameters:
tokenOwner
the address to query.
Returns: uint256
the number of tokens owned by this address.
function tokenOwnerOf(bytes32 tokenId) external view returns (address);
Returns the tokenOwner
address of the tokenId
token.
Parameters:
tokenId
the token to query.
Requirements:
tokenId
must exist
Returns: address
the token owner.
function tokenIdsOf(address tokenOwner) external view returns (bytes32[] memory);
Returns the list of tokenIds
for the tokenOwner
address.
Parameters:
tokenOwner
the address to query.
Returns: bytes32[]
the list of owned token ids.
function authorizeOperator(address operator, bytes32 tokenId, bytes memory operatorNotificationData) external;
Makes operator
address an operator of tokenId
.
MUST emit an OperatorAuthorizationChanged event.
Parameters:
operator
the address to authorize as an operator.tokenId
the token to enable operator status to.operatorNotificationData
the data to send when notifying the operator via LSP1.
Requirements:
tokenId
must exist.operator
cannot be already authorized for the same tokenId.- caller must be current
tokenOwner
oftokenId
. operator
cannot be calling address.operator
cannot be the zero address.
LSP1 Hooks:
-
If the operator is a contract that supports LSP1 interface, it SHOULD call operator's [
universalReceiver(...)
] function with the parameters below:typeId
:keccak256('LSP8Tokens_OperatorNotification')
>0x8a1c15a8799f71b547e08e2bcb2e85257e81b0a07eee2ce6712549eef1f00970
data
: The data sent SHOULD be abi encoded and contain thetokenOwner
(address),tokenId
(bytes32),isAuthorized
(boolean), and theoperatorNotificationData
(bytes) respectively.
function revokeOperator(address operator, bytes32 tokenId, bool notify, bytes memory operatorNotificationData) external;
Removes operator
address as an operator of tokenId
.
MUST emit a OperatorRevoked event.
Parameters:
operator
the address to revoke as an operator.tokenId
the token to disable operator status to.notify
the boolean indicating whether to notify the operator via LSP1 or not.operatorNotificationData
the data to send when notifying the operator via LSP1.
Requirements:
tokenId
must exist.operator
must be authorized fortokenId
.- caller must be current
tokenOwner
or theoperator
oftokenId
. operator
cannot be the zero address.
LSP1 Hooks:
-
If the
notify
boolean is set totrue
and the operator is a contract that supports LSP1 interface, it SHOULD call operator's [universalReceiver(...)
] function with the parameters below:typeId
:keccak256('LSP8Tokens_OperatorNotification')
>0x8a1c15a8799f71b547e08e2bcb2e85257e81b0a07eee2ce6712549eef1f00970
data
: The data sent SHOULD be abi encoded and contain thetokenOwner
(address),tokenId
(bytes32),isAuthorized
(boolean), and theoperatorNotificationData
(bytes) respectively.
function isOperatorFor(address operator, bytes32 tokenId) external view returns (bool);
Returns whether operator
address is an operator of tokenId
.
Operators can send and burn tokens on behalf of their owners. The tokenOwner is their own operator.
Parameters:
operator
the address to query operator status for.tokenId
the token to query.
Requirements:
tokenId
must exist
Returns: bool
, TRUE if operator
address is an operator of tokenId
, FALSE otherwise.
function getOperatorsOf(bytes32 tokenId) external view returns (address[] memory);
Returns all operator
addresses of tokenId
.
Parameters:
tokenId
the token to query.
Requirements:
tokenId
must exist
Returns: address[]
the list of operators.
function transfer(address from, address to, bytes32 tokenId, bool force, bytes memory data) external;
Transfers tokenId
token from from
to to
. The force
parameter will be used when notifying the token sender and receiver.
MUST emit a Transfer event when transfer was successful. MUST emit a OperatorRevoked to clear the past operators.
Parameters:
from
the sending address.to
the receiving address.tokenId
the token to transfer.force
when set to TRUE,to
may be any address; when set to FALSEto
must be a contract that supports LSP1 UniversalReceiver and successfully processes a call touniversalReceiver(bytes32 typeId, bytes memory data)
.data
additional data the caller wants included in the emitted event, and sent in the hooks tofrom
andto
addresses.
Requirements:
from
cannot be the zero address.to
cannot be the zero address.tokenId
token must be owned byfrom
.- If the caller is not
from
, it must be an operator oftokenId
.
LSP1 Hooks:
-
If the token sender is a contract that supports LSP1 interface, it SHOULD call the token sender's [
universalReceiver(...)
] function with the parameters below:typeId
:keccak256('LSP8Tokens_SenderNotification')
=0xb23eae7e6d1564b295b4c3e3be402d9a2f0776c57bdf365903496f6fa481ab00
data
: The data sent SHOULD be ABI encoded and contain thecaller
(address),sender
(address),receiver
(address),tokenId
(bytes32) and thedata
(bytes) respectively.
-
If the token recipient is a contract that supports LSP1 interface, it SHOULD call the token recipient's [
universalReceiver(...)
] function with the parameters below:typeId
:keccak256('LSP8Tokens_RecipientNotification')
=0x0b084a55ebf70fd3c06fd755269dac2212c4d3f0f4d09079780bfa50c1b2984d
data
: The data sent SHOULD be ABI encoded and contain thecaller
(address),sender
(address),receiver
(address),tokenId
(bytes32) and thedata
(bytes) respectively.
Note: LSP1 Hooks MUST be implemented in any type of token transfer (mint, transfer, burn, transferBatch).
function transferBatch(address[] memory from, address[] memory to, bytes32[] memory tokenId, bool[] memory force, bytes[] memory data) external;
Transfers many tokens based on the list from
, to
, tokenId
. If any transfer fails, the call will revert.
MUST emit a Transfer event for each transferred token. MUST emit a OperatorRevoked to clear the past operators for each transferred token.
Parameters:
from
the list of sending addresses.to
the list of receiving addresses.tokenId
the list of tokens to transfer.force
when set to TRUE,to
may be any address; when set to FALSEto
must be a contract that supports LSP1 UniversalReceiver and successfully processes a call touniversalReceiver(bytes32 typeId, bytes memory data)
.data
the list of additional data the caller wants included in the emitted event, and sent in the hooks tofrom
andto
addresses.
Requirements:
from
,to
,tokenId
,force
, anddata
lists are the same length.- no values in
from
can be the zero address. - no values in
to
can be the zero address. - each
tokenId
token must be owned byfrom
. - If the caller is not
from
, it must be an operator of eachtokenId
.
function getDataForTokenId(bytes32 tokenId, bytes32 dataKey) external view returns (bytes memory dataValue)
Gets the data set for the given data key for a specific tokenId.
Parameters:
tokenId
: the tokenId to retrieve data for.dataKey
: the data key which value to retrieve.
Returns: bytes
, The data for the requested data key.
function getDataBatchForTokenIds(bytes32[] memory tokenIds, bytes32[] memory dataKeys) external view returns(bytes[] memory dataValues)
Gets array of data at multiple given data keys for given tokenIds.
Parameters:
tokenIds
: the tokenIds to retrieve data for.dataKeys
: the data keys which values to retrieve.
Returns: bytes[]
, array of data values for the requested data keys.
function setDataForTokenId(bytes32 tokenId, bytes32 dataKey, bytes memory dataValue) external;
Sets data as bytes in the storage for a single data key for a tokenId.
Parameters:
tokenId
: the tokenId to set data for.dataKey
: the data key which value to set.dataValue
: the data to store.
Requirements:
- MUST only be called by the current owner of the contract.
Triggers Event: TokenIdDataChanged
function setDataBatchForTokenIds(bytes32[] memory tokenIds, bytes32[] memory dataKeys, bytes[] memory dataValues) external
Sets array of data at multiple data keys for multiple tokenIds.
Parameters:
tokenIds
: the tokenIds to set data for.dataKeys
: the data keys which values to set.dataValues
: the array of bytes to set.
Requirements:
- Array parameters MUST have the same length.
- MUST only be called by the current owner of the contract.
Triggers Event: TokenIdDataChanged on each iteration
function batchCalls(bytes[] memory data) external returns (bytes[] memory results);
Enables the execution of a batch of encoded function calls on the current contract in a single transaction, provided as an array of bytes.
MUST use the [DELEGATECALL] opcode to execute each call in the same context of the current contract.
Parameters:
data
: an array of encoded function calls to be executed on the current contract.
The data field can be:
- an array of ABI-encoded function calls such as an array of ABI-encoded
transfer
,authorizeOperator
,balanceOf
or any LSP8 functions. - an array of bytes which will resolve to the fallback function to be checked for an extension.
Requirements:
- MUST NOT be payable.
Returns: results
, an array of bytes containing the return values of each executed function call.
event TokenIdDataChanged(bytes32 indexed tokenId, bytes32 indexed dataKey, bytes dataValue);
MUST be emitted when dataValue
is set as a value for dataValue
for the tokenId
.
event Transfer(address operator, address indexed from, address indexed to, bytes32 indexed tokenId, bool force, bytes data);
MUST be emitted when tokenId
token is transferred from from
to to
.
event OperatorAuthorizationChanged(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData);
MUST be emitted when tokenOwner
enables operator
for tokenId
.
event OperatorRevoked(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData);
MUST be emitted when tokenOwner
disables operator
for tokenId
.
The LSP8-IdentifiableDigitalAsset expect the usage of LSP4-DigitalAsset-Metadata to store the metadata of the asset, as well as defining standard data keys to store LSP8 specific metadata.
Data keys such as LSP8TokenIdFormat
or LSP4Metadata
can be stored:
- either for the whole contract using
setData(..)
- per NFT / tokenId using
setDataForTokenId(..)
.
For instance, to set metadata for each specific tokenId, set the LSP4Metadata
data key for each tokenId using setDataForTokenId(..)
function.
{
"name": "LSP8TokenIdFormat",
"key": "0xf675e9361af1c1664c1868cfa3eb97672d6b1a513aa5b81dec34c9ee330e818d",
"keyType": "Singleton",
"valueType": "uint256",
"valueContent": "Number"
}
The LSP8-IdentifiableDigitalAsset standard, defines each tokenId
as bytes32
, this data key describes the format of the tokenId
and how to parse it and can take one of the following values described in the table below.
Value | Format | Representation | Description |
---|---|---|---|
0 |
uint256 |
Number | each NFT is parsed as a unique number. |
1 |
string |
String | each NFT is parsed as a unique name (as a short UTF-8 encoded string, no more than 32 characters long) |
2 |
address |
Smart Contract | each NFT is parsed as its own smart contract that can hold its own logic and metadata (e.g ERC725Y compatible). |
3 |
bytes32 |
Unique Bytes | each NFT is parsed as a 32 bytes long unique identifier. |
4 |
bytes32 |
Hash Digest | each NFT is parsed as a 32 bytes hash digest. This can be used as the hash of a very long string representing the tokenId. |
The standard allows for Mixed formats. In a mixed format scenario, while there's a default format for the collection, individual tokenIds can have unique formats. This concept is represented with special values ranging from 100 to 104.
Value | Format | Description |
---|---|---|
100 |
Mixed with default as uint256 |
Default NFT is parsed as a unique number with querying the LSP8TokenIdFormat for each tokenId . |
101 |
Mixed with default as string |
Default NFT is parsed as a unique name (as a short UTF-8 encoded string, no more than 32 characters long) with querying the LSP8TokenIdFormat for each tokenId . |
102 |
Mixed with default as address |
Default NFT is parsed as its own smart contract that can hold its own logic and metadata (e.g ERC725Y compatible) with querying the LSP8TokenIdFormat for each tokenId . . |
103 |
Mixed with default as bytes32 unique bytes |
Default NFT is parsed as a 32 bytes long unique identifier with querying the LSP8TokenIdFormat for each tokenId . |
104 |
Mixed with default as bytes32 hash digest |
Default NFT is parsed as a 32 bytes hash digestwith querying the LSP8TokenIdFormat for each tokenId . |
Setting and Querying Token ID Formats:
- Setting a tokenId format: Use
setDataForTokenId(..)
to set theLSP8TokenIdFormat
for a specific tokenId. - Querying a tokenId format: Use
getDataForTokenId(..)
to retrieve theLSP8TokenIdFormat
for a specific tokenId.
Example Scenario:
Given a collection where the LSP8TokenIdFormat
is set to 101
, indicating that tokenIds are primarily formatted as strings. However, for a specific tokenId that needs to be represented as a number, its LSP8TokenIdFormat
would be set to 100 using setDataForTokenId(..)
.
This change signifies that while the default format for the collection is string (101
), this particular tokenId deviates from the norm and is formatted as a number (100
), exemplifying the mixed format approach.
If the value for the
LSP8TokenIdFormat
for a specific tokenId is 0, it means the value is non-set (default value), and should not be treated as LSP8TokenIdFormat = 0 (parsing as a number).
A tokenId
is always represented as a bytes32
value. Depending on the tokenId formats defined above, the padding of the bytes32
value is different.
LSP8TokenIdFormat | Left padded | Right padded | Padding rule to convert to bytes32 |
---|---|---|---|
0 or 100 - uint256 - Number |
✔️ | For tokenId number 5 -> 0x0000000000000000000000000000000000000000000000000000000000000005 |
|
1 or 101 - string - String |
✔️ | For tokenId my-nft -> 0x6d792d6e66740000000000000000000000000000000000000000000000000000 (each character encoded as UTF-8 hex) |
|
2 or 102 - address - Smart Contract |
✔️ | For tokenId metadata contract at address 0x8ae2dD3E422530b5c2FC1061e6b5f43f5677033f -> 0x0000000000000000000000008ae2dD3E422530b5c2FC1061e6b5f43f5677033f |
|
3 or 103 - bytes32 - Unique Bytes |
✔️ | For any bytes less than 32 bytes like tokenId 0xaabbccddee -> 0xaabbccddee000000000000000000000000000000000000000000000000000000 |
|
4 or 104 - bytes32 - Hash Digest |
No padding applies, since the hash digest is always 32 bytes. For instance for tokenId keccak256('My NFT') -> 0x262a8c3566f2abe9247c206cf8d622e0a44ac99a7d54c23e212de32181cf185f |
This value must be padded according to the padding rules specified in the table above to generate the bytes32 tokenId
that will be passed as parameter to the functions below:
- when being transferred via
transfer(address,address,bytes32,bool,bytes)
) ortransferBatch(address[],address[],bytes32[],bool,bytes[])
. - when querying the owner for the tokenId via
tokenOwnerOf(bytes32)
. - when performing operators related operations via
authorizeOperator(address,bytes32,bytes)
,revokeOperator(address,bytes32,bool,bytes)
,isOperatorFor(address,bytes32)
,getOperatorsOf(bytes32)
.
Requirements:
- This MUST NOT be changeable, and set only during initialization of the LSP8 token contract.
{
"name": "LSP8TokenMetadataBaseURI",
"key": "0x1a7628600c3bac7101f53697f48df381ddc36b9015e7d7c9c5633d1252aa2843",
"keyType": "Singleton",
"valueType": "bytes",
"valueContent": "VerifiableURI"
}
This data key defines the base URI for the metadata of each tokenId
s present in the LSP8 contract.
The complete URI that points to the metadata of a specific tokenId MUST be formed by concatenating this base URI with the tokenId
.
As {LSP8TokenMetadataBaseURI}{tokenId}
. The protocol can be http, https, ipfs and so on similar to other VerifiableURI values.
address
(2
), the tokenId
part SHOULD be in lower case to make it unique and avoid interfaces to have to know multiple versions of the URI with different casings (e.g: http://mybase.uri/0x43fb7ab43a3a32f1e2d5326b651bbae713b02429
, or http://mybase.uri/0x43FB7AB43A3A32F1E2D5326B651BBAE713B02429
, or http://mybase.uri/0x43fB7aB43A3a32F1E2d5326b651BBAe713B02429
).
bytes32
version of tokenId format (3
and 4
), the tokenId part MUST be lowercase.
string
version of the tokenId (2
), the tokenId part MUST be in the same casing as defined by the string that represents the tokenId. Otherwise, changing the casing is equivalent to using a different tokenId. The tokenId is not limited in casing but needs to contain valid UTF-8 and needs to be encoded using url-encoding i.e. encodeURI(). For example: A tokenId containing something/Veryሴ⍅ Cool
should be encoded as something/Very%E1%88%B4%E2%8D%85%20Cool
as in the following example.
> baseURL + encodeURI("something/Very\u1234\u2345 Cool")
'https://mybase.uri/something/Very%E1%88%B4%E2%8D%85%20Cool'
- LSP8TokenIdFormat
0
(=uint256
)
e.g.https://mybase.uri/1234
- LSP8TokenIdFormat
1
(=string
)
e.g.https://mybase.uri/name-of-the-nft
- LSP8TokenIdFormat
2
(=address
)
e.g.https://mybase.uri/0x43fb7ab43a3a32f1e2d5326b651bbae713b02429
- LSP8TokenIdFormat
3
or4
(=bytes32
)
e.g.https://mybase.uri/e5fe3851d597a3aa8bbdf8d8289eb9789ca2c34da7a7c3d0a7c442a87b81d5c2
Some Base URIs could be alterable, for example in the case of NFTs that need their metadata to change over time.
When the LSP8 contract uses the tokenId format 2
(= address
), each tokenId minted is an ERC725Y smart contract that can have its own metadata.
We refer to this contract as the tokenId metadata contract.
In this case, each tokenId present in the LSP8 contract references another ERC725Y contract.
The tokenId metadata contract SHOULD contain the following ERC725Y data key in its storage.
This data key stores the address of the LSP8 contract that minted this specific tokenId
(defined by the address of the tokenId metadata contract).
It is a reference back to the LSP8 Collection it comes from.
If the LSP8ReferenceContract
data key is set, it MUST NOT be changeable.
{
"name": "LSP8ReferenceContract",
"key": "0x708e7b881795f2e6b6c2752108c177ec89248458de3bf69d0d43480b3e5034e6",
"keyType": "Singleton",
"valueType": "(address,bytes32)",
"valueContent": "(Address,bytes32)"
}
The metadata for a specific of a uniquely identifiable digital asset (when this tokenId is represented by its own ERC725Y contract) can follow the JSON format of the LSP4Metadata
data key.
This JSON format includes an "attributes"
field to describe unique properties of the tokenId.
There should be a base token standard that allows tracking unique assets for the LSP ecosystem of contracts, which will allow common tooling and clients to be built. Existing tools and clients that expect ERC721 can be made to work with this standard by using "compatibility" contract extensions that match the desired interface.
Every token is identified by a unique bytes32 tokenId
which SHALL NOT change for the life of the contract. The pair (contract address, uint256 tokenId)
is globally unique and a fully-qualified identifier for a specific asset on-chain. While some implementations may find it convenient to use the tokenId as an uint256
that is incremented for each minted token, callers SHALL NOT assume that tokenIds have any specific pattern to them, and MUST treat the tokenId as a "black box". Also note that a tokenId MAY become invalid (when burned).
The choice of bytes32 tokenId
allows a wide variety of applications including numbers, contract addresses, and hashed values (ie. serial numbers).
To clarify the ability of an address to access tokens from another address, operator
was chosen as the name for functions, events and variables in all cases. This is originally from ERC777 standard and replaces the approve
functionality from ERC721.
There is only one transfer function, which is aware of operators. This deviates from ERC721 and ERC777 which added functions specifically for the token owner to use, and for those with access to tokens. By having a single function to call this makes it simple to move tokens, and the caller will be exposed in the Transfer
event as an indexed value.
When a token is changing owners (minting, transferring, burning) an attempt is made to notify the token sender and receiver using LSP1 UniversalReceiver interface. The implementation uses _notifyTokenSender
and _notifyTokenReceiver
as the internal functions to process this.
The force
parameter sent during function transfer
SHOULD be used when notifying the token receiver, to determine if it must support LSP1 UniversalReceiver. This is used to prevent accidental token transfers, which may results in lost tokens: non-contract addresses could be a copy paste issue, contracts not supporting LSP1 UniversalReceiver might not be able to move tokens.
An implementation can be found in the lukso-network/lsp-smart-contracts.
ERC725Y JSON Schema LSP8IdentifiableDigitalAsset
:
[
{
"name": "LSP8TokenIdFormat",
"key": "0xf675e9361af1c1664c1868cfa3eb97672d6b1a513aa5b81dec34c9ee330e818d",
"keyType": "Singleton",
"valueType": "uint256",
"valueContent": "Number"
},
{
"name": "LSP8TokenMetadataBaseURI",
"key": "0x1a7628600c3bac7101f53697f48df381ddc36b9015e7d7c9c5633d1252aa2843",
"keyType": "Singleton",
"valueType": "bytes",
"valueContent": "VerifiableURI"
},
{
"name": "LSP8ReferenceContract",
"key": "0x708e7b881795f2e6b6c2752108c177ec89248458de3bf69d0d43480b3e5034e6",
"keyType": "Singleton",
"valueType": "(address,bytes32)",
"valueContent": "(Address,bytes32)"
}
]
interface ILSP8 is /* IERC165 */ {
// ERC173
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function owner() external view returns (address);
function transferOwnership(address newOwner) external; // onlyOwner
function renounceOwnership() external; // onlyOwner
// ERC725Y
event DataChanged(bytes32 indexed dataKey, bytes dataValue);
function getData(bytes32 dataKey) external view returns (bytes memory value);
function setData(bytes32 dataKey, bytes memory value) external; // onlyOwner
function getDataBatch(bytes32[] memory dataKeys) external view returns (bytes[] memory values);
function setDataBatch(bytes32[] memory dataKeys, bytes[] memory values) external; // onlyOwner
// LSP8
event Transfer(address operator, address indexed from, address indexed to, bytes32 indexed tokenId, bool force, bytes data);
event OperatorAuthorizationChanged(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData);
event OperatorRevoked(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData);
event TokenIdDataChanged(bytes32 indexed tokenId, bytes32 indexed dataKey, bytes dataValue);
function getDataForTokenId(bytes32 tokenId, bytes32 dataKey) external view returns (bytes memory dataValue);
function setDataForTokenId(bytes32 tokenId, bytes32 dataKey, bytes memory dataValue) external; // onlyOwner
function getDataBatchForTokenIds(bytes32[] memory tokenIds, bytes32[] memory dataKeys) external view returns (bytes[] memory dataValues);
function setDataBatchForTokenIds(bytes32[] memory tokenIds, bytes32[] memory dataKeys, bytes[] memory dataValues) external; // onlyOwner
function totalSupply() external view returns (uint256);
function balanceOf(address tokenOwner) external view returns (uint256);
function tokenOwnerOf(bytes32 tokenId) external view returns (address);
function tokenIdsOf(address tokenOwner) external view returns (bytes32[] memory);
function authorizeOperator(address operator, bytes32 tokenId, bytes memory operatorNotificationData) external;
function revokeOperator(address operator, bytes32 tokenId, bool notify, bytes memory operatorNotificationData) external;
function isOperatorFor(address operator, bytes32 tokenId) external view returns (bool);
function getOperatorsOf(bytes32 tokenId) external view returns (address[] memory);
function transfer(address from, address to, bytes32 tokenId, bool force, bytes memory data) external;
function transferBatch(address[] memory from, address[] memory to, bytes32[] memory tokenId, bool[] memory force, bytes[] memory data) external;
function batchCalls(bytes[] memory data) external returns (bytes[] memory results);
}
Copyright and related rights waived via CC0.