diff --git a/constants.ts b/constants.ts index 84a95de56..dec2e9594 100644 --- a/constants.ts +++ b/constants.ts @@ -180,6 +180,14 @@ export const ERC725YDataKeys = { LSP9: { SupportedStandards_LSP9: SupportedStandards.LSP9Vault.key, }, + LSP7: { + // keccak256('LSP7NonTransferable') + LSP7NonTransferable: '0xb48a0085fb2c841c614d8c650033c5b790f407c071b9176ca4f2003db3613a1f', + }, + LSP8: { + // keccak256('LSP8NonTransferable') + LSP8NonTransferable: '0x0c9f377ecc6456a1dd41eb14df797a662dd42fc0d4cbcc3f4532f70736eb56ba', + }, LSP10: { // keccak256('LSP10VaultsMap') + bytes2(0) LSP10VaultsMap: '0x192448c3c0f88c7f238c0000', diff --git a/contracts/LSP7DigitalAsset/LSP7Constants.sol b/contracts/LSP7DigitalAsset/LSP7Constants.sol index 24efb631b..f24dd314a 100644 --- a/contracts/LSP7DigitalAsset/LSP7Constants.sol +++ b/contracts/LSP7DigitalAsset/LSP7Constants.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.4; +// keccak256('LSP7NonTransferable') +bytes32 constant _LSP7_NON_TRANSFERABLE = 0xb48a0085fb2c841c614d8c650033c5b790f407c071b9176ca4f2003db3613a1f; + // --- ERC165 interface ids bytes4 constant _INTERFACEID_LSP7 = 0xda1f85e4; diff --git a/contracts/LSP7DigitalAsset/LSP7Errors.sol b/contracts/LSP7DigitalAsset/LSP7Errors.sol index b01947b81..a2e5d5ef7 100644 --- a/contracts/LSP7DigitalAsset/LSP7Errors.sol +++ b/contracts/LSP7DigitalAsset/LSP7Errors.sol @@ -68,3 +68,10 @@ error LSP7TokenOwnerCannotBeOperator(); * @dev Reverts when trying to decrease an operator's allowance to more than its current allowance. */ error LSP7DecreasedAllowanceBelowZero(); + +/** + * @dev Reverts when trying to edit the data key `LSP7NonTransferable` after the digital asset contract has been deployed. + * The `LSP7NonTransferable` data key is located inside the ERC725Y Data key-value store of the digital asset contract. + * It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + */ +error LSP7NonTransferableNotEditable(); diff --git a/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol b/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol new file mode 100644 index 000000000..91cfd8bd4 --- /dev/null +++ b/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {_LSP7_NON_TRANSFERABLE} from "../LSP7Constants.sol"; +import {LSP7NonTransferableNotEditable} from "../LSP7Errors.sol"; +import {LSP7DigitalAsset} from "../LSP7DigitalAsset.sol"; + +/** + * @dev LSP7 extension, adds the concept of a non-transferable token. + */ +contract LSP7NonTransferable is LSP7DigitalAsset { + /** + * @notice Deploying a `LSP7NonTransferable` token contract with: token name = `name_`, token symbol = `symbol_`, + * address `newOwner_` as the token contract owner, and _isNonDivisible_ = `isNonDivisible_`. + * Switch ON the non-transferable flag. + * + * @param name_ The name of the token. + * @param symbol_ The symbol of the token. + * @param newOwner_ The owner of the token contract. + * @param isNonDivisible_ Specify if the tokens from this contract can be divided in smaller units or not. + */ + constructor( + string memory name_, + string memory symbol_, + address newOwner_, + bool isNonDivisible_ + ) LSP7DigitalAsset(name_, symbol_, newOwner_, isNonDivisible_) { + // Set the non-transferable flag + super._setData(_LSP7_NON_TRANSFERABLE, hex"01"); + } + + /** + * @dev This function override the internal `_transfer` function to make it non-transferable + */ + function _transfer( + address /* from */, + address /* to */, + uint256 /* amount */, + bool /* allowNonLSP1Recipient */, + bytes memory /* data */ + ) internal virtual override { + revert("LSP7: Token is non-transferable"); + } + + /** + * @dev the ERC725Y data key `LSP7NonTransferable` cannot be changed + * via this function once the digital asset contract has been deployed. + * + * @notice This function override the _setData function to make the non-transferable flag not editable + */ + function _setData( + bytes32 dataKey, + bytes memory dataValue + ) internal virtual override { + if (dataKey == _LSP7_NON_TRANSFERABLE) { + revert LSP7NonTransferableNotEditable(); + } + super._setData(dataKey, dataValue); + } +} diff --git a/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferableInitAbstract.sol b/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferableInitAbstract.sol new file mode 100644 index 000000000..fab60f995 --- /dev/null +++ b/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferableInitAbstract.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {_LSP7_NON_TRANSFERABLE} from "../LSP7Constants.sol"; +import {LSP7NonTransferableNotEditable} from "../LSP7Errors.sol"; +import { + LSP7DigitalAssetInitAbstract +} from "../LSP7DigitalAssetInitAbstract.sol"; + +/** + * @dev LSP7 extension, adds the concept of a non-transferable token. + */ +abstract contract LSP7NonTransferableInitAbstract is + LSP7DigitalAssetInitAbstract +{ + /** + * @notice Initializing a `LSP7NonTransferable` token contract with: token name = `name_`, token symbol = `symbol_`, + * address `newOwner_` as the token contract owner, and _isNonDivisible_ = `isNonDivisible_`. + * Switch ON the non-transferable flag. + * + * @param name_ The name of the token. + * @param symbol_ The symbol of the token. + * @param newOwner_ The owner of the token contract. + * @param isNonDivisible_ Specify if the tokens from this contract can be divided in smaller units or not. + */ + function _initialize( + string memory name_, + string memory symbol_, + address newOwner_, + bool isNonDivisible_ + ) internal virtual override onlyInitializing { + // Set the non-transferable flag + super._setData(_LSP7_NON_TRANSFERABLE, hex"01"); + + LSP7DigitalAssetInitAbstract._initialize( + name_, + symbol_, + newOwner_, + isNonDivisible_ + ); + } + + /** + * @dev This function override the internal `_transfer` function to make it non-transferable + */ + function _transfer( + address /* from */, + address /* to */, + uint256 /* amount */, + bool /* allowNonLSP1Recipient */, + bytes memory /* data */ + ) internal virtual override { + revert("LSP7: Token is non-transferable"); + } + + /** + * @dev the ERC725Y data key `LSP7NonTransferable` cannot be changed + * via this function once the digital asset contract has been deployed. + * + * @notice This function override the _setData function to make the non-transferable flag not editable + */ + function _setData( + bytes32 dataKey, + bytes memory dataValue + ) internal virtual override { + if (dataKey == _LSP7_NON_TRANSFERABLE) { + revert LSP7NonTransferableNotEditable(); + } + super._setData(dataKey, dataValue); + } +} diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol index 7b2a603ba..c568f0a66 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol @@ -12,6 +12,9 @@ bytes12 constant _LSP8_METADATA_ADDRESS_KEY_PREFIX = 0x73dcc7c3c4096cdc7f8a0000; // bytes10(keccak256('LSP8MetadataJSON')) + bytes2(0) bytes12 constant _LSP8_METADATA_JSON_KEY_PREFIX = 0x9a26b4060ae7f7d5e3cd0000; +// keccak256('lsp8NonTransferable') +bytes32 constant _LSP8_NON_TRANSFERABLE = 0x0c9f377ecc6456a1dd41eb14df797a662dd42fc0d4cbcc3f4532f70736eb56ba; + // --- Token Hooks // keccak256('LSP8Tokens_SenderNotification') diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8Errors.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8Errors.sol index c69ce9198..1a3835f43 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8Errors.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8Errors.sol @@ -71,3 +71,10 @@ error LSP8NotifyTokenReceiverIsEOA(address tokenReceiver); * @dev reverts when trying to authorize or revoke the token's owner as an operator. */ error LSP8TokenOwnerCannotBeOperator(); + +/** + * @dev Reverts when trying to edit the data key `LSP8NonTransferable` after the digital asset contract has been deployed. + * The `LSP8NonTransferable` data key is located inside the ERC725Y Data key-value store of the digital asset contract. + * It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + */ +error LSP8NonTransferableNotEditable(); diff --git a/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol new file mode 100644 index 000000000..0d0f2f2ae --- /dev/null +++ b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {_LSP8_NON_TRANSFERABLE} from "../LSP8Constants.sol"; +import {LSP8NonTransferableNotEditable} from "../LSP8Errors.sol"; +import { + LSP8IdentifiableDigitalAsset +} from "../LSP8IdentifiableDigitalAsset.sol"; + +/** + * @dev LSP8 extension, adds the concept of a non-transferable token. + */ +contract LSP8NonTransferable is LSP8IdentifiableDigitalAsset { + /** + * @notice Deploying a `LSP8NonTransferable` token contract with: token name = `name_`, token symbol = `symbol_`, + * address `newOwner_` as the token contract owner. + * Switch ON the non-transferable flag. + * + * @param name_ The name of the token. + * @param symbol_ The symbol of the token. + * @param newOwner_ The owner of the token contract. + */ + constructor( + string memory name_, + string memory symbol_, + address newOwner_ + ) LSP8IdentifiableDigitalAsset(name_, symbol_, newOwner_) { + // Set the non-transferable flag + super._setData(_LSP8_NON_TRANSFERABLE, hex"01"); + } + + /** + * @dev This function override the internal `_transfer` function to make it non-transferable + */ + function _transfer( + address /* from */, + address /* to */, + bytes32 /* tokenId */, + bool /* allowNonLSP1Recipient */, + bytes memory /* data */ + ) internal virtual override { + revert("LSP8: Token is non-transferable"); + } + + /** + * @dev the ERC725Y data key `LSP8NonTransferable` cannot be changed + * via this function once the digital asset contract has been deployed. + * + * @notice This function override the _setData function to make the non-transferable flag not editable + */ + function _setData( + bytes32 dataKey, + bytes memory dataValue + ) internal virtual override { + if (dataKey == _LSP8_NON_TRANSFERABLE) { + revert LSP8NonTransferableNotEditable(); + } + super._setData(dataKey, dataValue); + } +} diff --git a/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferableInitAbstract.sol b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferableInitAbstract.sol new file mode 100644 index 000000000..ec15d471a --- /dev/null +++ b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferableInitAbstract.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import {_LSP8_NON_TRANSFERABLE} from "../LSP8Constants.sol"; +import {LSP8NonTransferableNotEditable} from "../LSP8Errors.sol"; +import { + LSP8IdentifiableDigitalAssetInitAbstract +} from "../LSP8IdentifiableDigitalAssetInitAbstract.sol"; + +/** + * @dev LSP8 extension, adds the concept of a non-transferable token. + */ +abstract contract LSP8NonTransferableInitAbstract is + LSP8IdentifiableDigitalAssetInitAbstract +{ + /** + * @notice Initializing a `LSP8NonTransferable` token contract with: token name = `name_`, token symbol = `symbol_`, + * address `newOwner_` as the token contract owner. + * Switch ON the non-transferable flag. + * + * @param name_ The name of the token. + * @param symbol_ The symbol of the token. + * @param newOwner_ The owner of the token contract. + */ + function _initialize( + string memory name_, + string memory symbol_, + address newOwner_ + ) internal virtual override onlyInitializing { + // Set the non-transferable flag + super._setData(_LSP8_NON_TRANSFERABLE, hex"01"); + + LSP8IdentifiableDigitalAssetInitAbstract._initialize( + name_, + symbol_, + newOwner_ + ); + } + + /** + * @dev This function override the internal `_transfer` function to make it non-transferable + */ + function _transfer( + address /* from */, + address /* to */, + bytes32 /* tokenId */, + bool /* allowNonLSP1Recipient */, + bytes memory /* data */ + ) internal virtual override { + revert("LSP8: Token is non-transferable"); + } + + /** + * @dev the ERC725Y data key `LSP8NonTransferable` cannot be changed + * via this function once the digital asset contract has been deployed. + * + * @notice This function override the _setData function to make the non-transferable flag not editable + */ + function _setData( + bytes32 dataKey, + bytes memory dataValue + ) internal virtual override { + if (dataKey == _LSP8_NON_TRANSFERABLE) { + revert LSP8NonTransferableNotEditable(); + } + super._setData(dataKey, dataValue); + } +} diff --git a/contracts/Mocks/Tokens/LSP7NonTransferableInitTester.sol b/contracts/Mocks/Tokens/LSP7NonTransferableInitTester.sol new file mode 100644 index 000000000..74c6ea66d --- /dev/null +++ b/contracts/Mocks/Tokens/LSP7NonTransferableInitTester.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +// modules +import { + LSP7NonTransferableInitAbstract +} from "../../LSP7DigitalAsset/extensions/LSP7NonTransferableInitAbstract.sol"; + +contract LSP7NonTransferableInitTester is LSP7NonTransferableInitAbstract { + function initialize( + string memory name_, + string memory symbol_, + address newOwner_, + bool isNonDivisible_ + ) public virtual initializer { + LSP7NonTransferableInitAbstract._initialize( + name_, + symbol_, + newOwner_, + isNonDivisible_ + ); + } + + function mint( + address to, + uint256 amount, + bool allowNonLSP1Recipient, + bytes memory data + ) public virtual { + _mint(to, amount, allowNonLSP1Recipient, data); + } +} diff --git a/contracts/Mocks/Tokens/LSP7NonTransferableTester.sol b/contracts/Mocks/Tokens/LSP7NonTransferableTester.sol new file mode 100644 index 000000000..857c46cf3 --- /dev/null +++ b/contracts/Mocks/Tokens/LSP7NonTransferableTester.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +// modules +import { + LSP7NonTransferable +} from "../../LSP7DigitalAsset/extensions/LSP7NonTransferable.sol"; + +contract LSP7NonTransferableTester is LSP7NonTransferable { + constructor( + string memory name_, + string memory symbol_, + address newOwner_, + bool isNonDivisible_ + ) LSP7NonTransferable(name_, symbol_, newOwner_, isNonDivisible_) {} + + function mint( + address to, + uint256 amount, + bool allowNonLSP1Recipient, + bytes memory data + ) public virtual { + _mint(to, amount, allowNonLSP1Recipient, data); + } +} diff --git a/contracts/Mocks/Tokens/LSP8NonTransferableInitTester.sol b/contracts/Mocks/Tokens/LSP8NonTransferableInitTester.sol new file mode 100644 index 000000000..4491eff43 --- /dev/null +++ b/contracts/Mocks/Tokens/LSP8NonTransferableInitTester.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +// modules +import { + LSP8NonTransferableInitAbstract +} from "../../LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferableInitAbstract.sol"; + +contract LSP8NonTransferableInitTester is LSP8NonTransferableInitAbstract { + function initialize( + string memory name_, + string memory symbol_, + address newOwner_ + ) public virtual initializer { + LSP8NonTransferableInitAbstract._initialize(name_, symbol_, newOwner_); + } + + function mint( + address to, + bytes32 tokenId, + bool allowNonLSP1Recipient, + bytes memory data + ) public virtual { + _mint(to, tokenId, allowNonLSP1Recipient, data); + } +} diff --git a/contracts/Mocks/Tokens/LSP8NonTransferableTester.sol b/contracts/Mocks/Tokens/LSP8NonTransferableTester.sol new file mode 100644 index 000000000..fa7a8b7c1 --- /dev/null +++ b/contracts/Mocks/Tokens/LSP8NonTransferableTester.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.4; + +// modules +import { + LSP8NonTransferable +} from "../../LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol"; + +contract LSP8NonTransferableTester is LSP8NonTransferable { + constructor( + string memory name_, + string memory symbol_, + address newOwner_ + ) LSP8NonTransferable(name_, symbol_, newOwner_) {} + + function mint( + address to, + bytes32 tokenId, + bool allowNonLSP1Recipient, + bytes memory data + ) public virtual { + _mint(to, tokenId, allowNonLSP1Recipient, data); + } +} diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.md new file mode 100644 index 000000000..c0ef7fc3e --- /dev/null +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.md @@ -0,0 +1,1290 @@ + + + +# LSP7NonTransferable + +:::info Standard Specifications + +[`LSP-7-DigitalAsset`](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md) + +::: +:::info Solidity implementation + +[`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) + +::: + +LSP7 extension, adds the concept of a non-transferable token. + +## Public Methods + +Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts. +When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally. + +### constructor + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#constructor) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) + +::: + +```solidity +constructor( + string name_, + string symbol_, + address newOwner_, + bool isNonDivisible_ +); +``` + +_Deploying a `LSP7NonTransferable` token contract with: token name = `name_`, token symbol = `symbol_`, address `newOwner_` as the token contract owner, and *isNonDivisible* = `isNonDivisible_`. Switch ON the non-transferable flag._ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `name_` | `string` | The name of the token. | + +| `symbol_` | `string` | The symbol of the token. | + +| `newOwner_` | `address` | The owner of the token contract. | + +| `isNonDivisible_` | `bool` | Specify if the tokens from this contract can be divided in smaller units or not. | + +
+ +### authorizeOperator + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#authorizeoperator) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `authorizeOperator(address,uint256)` +- Function selector: `0x47980aa3` + +::: + +:::danger + +To avoid front-running and Allowance Double-Spend Exploit when increasing or decreasing the authorized amount of an operator, it is advised to: 1. either call {revokeOperator} first, and then re-call {authorizeOperator} with the new amount. 2. or use the non-standard functions {increaseAllowance} or {decreaseAllowance}. For more information, see: https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/ + +::: + +```solidity +function authorizeOperator( + address operator, + uint256 amount +) external nonpayable; +``` + +Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | The address to authorize as an operator. | + +| `amount` | `uint256` | The allowance amount of tokens operator has access to. | + +
+ +### authorizedAmountFor + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#authorizedamountfor) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `authorizedAmountFor(address,address)` +- Function selector: `0x65aeaa95` + +::: + +```solidity +function authorizedAmountFor( + address operator, + address tokenOwner +) external view returns (uint256); +``` + +Get the amount of tokens `operator` address has access to from `tokenOwner`. Operators can send and burn tokens on behalf of their owners. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | The operator's address to query the authorized amount for. | + +| `tokenOwner` | `address` | The token owner that `operator` has allowance on. | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `uint256` | The amount of tokens the `operator`'s address has access on the `tokenOwner`'s balance. | + +
+ +### balanceOf + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#balanceof) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `balanceOf(address)` +- Function selector: `0x70a08231` + +::: + +```solidity +function balanceOf(address tokenOwner) external view returns (uint256); +``` + +Get the number of tokens owned by `tokenOwner`. If the token is divisible (the [`decimals`](#decimals) function returns `18`), the amount returned should be divided by 1e18 to get a better picture of the actual balance of the `tokenOwner`. _Example:_ `balanceOf(someAddress) -> 42_000_000_000_000_000_000 / 1e18 = 42 tokens` + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenOwner` | `address` | The address of the token holder to query the balance for. | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `uint256` | The amount of tokens owned by `tokenOwner`. | + +
+ +### decimals + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#decimals) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `decimals()` +- Function selector: `0x313ce567` + +::: + +```solidity +function decimals() external view returns (uint8); +``` + +Returns the number of decimals used to get its user representation. If the asset contract has been set to be non-divisible via the `isNonDivisible_` parameter in the `constructor`, the decimals returned wiil be `0`. Otherwise `18` is the common value. + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `uint8` | the number of decimals. If `0` is returned, the asset is non-divisible. | + +
+ +### decreaseAllowance + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#decreaseallowance) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `decreaseAllowance(address,uint256)` +- Function selector: `0xa457c2d7` + +::: + +:::info + +This is a non-standard function, not part of the LSP7 standard interface. It has been added in the LSP7 contract implementation so that it can be used as a prevention mechanism against the double spending allowance vulnerability. + +::: + +```solidity +function decreaseAllowance( + address operator, + uint256 substractedAmount +) external nonpayable; +``` + +_Decrease the allowance of `operator` by -`substractedAmount`_ + +Atomically decreases the allowance granted to `operator` by the caller. This is an alternative approach to [`authorizeOperator`](#authorizeoperator) that can be used as a mitigation for the double spending allowance problem. + +
+ +**Requirements:** + +- `operator` cannot be the zero address. +- `operator` must have allowance for the caller of at least `substractedAmount`. + +
+ +
+ +**Emitted events:** + +- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. +- [`RevokeOperator`](#revokeoperator) event if `substractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. + +
+ +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | the operator to decrease allowance for `msg.sender` | + +| `substractedAmount` | `uint256` | the amount to decrease by in the operator's allowance. | + +
+ +### getData + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#getdata) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `getData(bytes32)` +- Function selector: `0x54f6127f` + +::: + +```solidity +function getData(bytes32 dataKey) external view returns (bytes dataValue); +``` + +_Gets singular data at a given `dataKey`_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKey` | `bytes32` | The key which value to retrieve | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataValue` | `bytes` | The data stored at the key | + +
+ +### getDataBatch + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#getdatabatch) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `getDataBatch(bytes32[])` +- Function selector: `0xdedff9c6` + +::: + +```solidity +function getDataBatch( + bytes32[] dataKeys +) external view returns (bytes[] dataValues); +``` + +_Gets array of data for multiple given keys_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKeys` | `bytes32[]` | The array of keys which values to retrieve | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataValues` | `bytes[]` | The array of data stored at multiple keys | + +
+ +### increaseAllowance + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#increaseallowance) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `increaseAllowance(address,uint256)` +- Function selector: `0x39509351` + +::: + +:::info + +This is a non-standard function, not part of the LSP7 standard interface. It has been added in the LSP7 contract implementation so that it can be used as a prevention mechanism against double spending allowance vulnerability. + +::: + +```solidity +function increaseAllowance( + address operator, + uint256 addedAmount +) external nonpayable; +``` + +_Increase the allowance of `operator` by +`addedAmount`_ + +Atomically increases the allowance granted to `operator` by the caller. This is an alternative approach to [`authorizeOperator`](#authorizeoperator) that can be used as a mitigation for the double spending allowance problem. + +
+ +**Requirements:** + +- `operator` cannot be the same address as `msg.sender` +- `operator` cannot be the zero address. + +
+ +
+ +**Emitted events:** + +- [`AuthorizedOperator`](#authorizedoperator) indicating the updated allowance + +
+ +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | the operator to increase the allowance for `msg.sender` | + +| `addedAmount` | `uint256` | the additional amount to add on top of the current operator's allowance | + +
+ +### owner + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#owner) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `owner()` +- Function selector: `0x8da5cb5b` + +::: + +```solidity +function owner() external view returns (address); +``` + +Returns the address of the current owner. + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `address` | - | + +
+ +### renounceOwnership + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#renounceownership) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `renounceOwnership()` +- Function selector: `0x715018a6` + +::: + +```solidity +function renounceOwnership() external nonpayable; +``` + +Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. + +
+ +### revokeOperator + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokeoperator) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `revokeOperator(address)` +- Function selector: `0xfad8b32a` + +::: + +```solidity +function revokeOperator(address operator) external nonpayable; +``` + +Removes the `operator` address as an operator of callers tokens, disallowing it to send any amount of tokens on behalf of the token owner (the caller of the function `msg.sender`). See also [`authorizedAmountFor`](#authorizedamountfor). + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | The address to revoke as an operator. | + +
+ +### setData + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#setdata) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `setData(bytes32,bytes)` +- Function selector: `0x7f23690c` + +::: + +```solidity +function setData(bytes32 dataKey, bytes dataValue) external payable; +``` + +_Sets singular data for a given `dataKey`_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKey` | `bytes32` | The key to retrieve stored value | + +| `dataValue` | `bytes` | The value to set SHOULD only be callable by the owner of the contract set via ERC173 The function is marked as payable to enable flexibility on child contracts If the function is not intended to receive value, an additional check should be implemented to check that value equal 0. Emits a {DataChanged} event. | + +
+ +### setDataBatch + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#setdatabatch) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `setDataBatch(bytes32[],bytes[])` +- Function selector: `0x97902421` + +::: + +```solidity +function setDataBatch(bytes32[] dataKeys, bytes[] dataValues) external payable; +``` + +Sets array of data for multiple given `dataKeys` SHOULD only be callable by the owner of the contract set via ERC173 The function is marked as payable to enable flexibility on child contracts If the function is not intended to receive value, an additional check should be implemented to check that value equal + +0. Emits a [`DataChanged`](#datachanged) event. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKeys` | `bytes32[]` | The array of data keys for values to set | + +| `dataValues` | `bytes[]` | The array of values to set | + +
+ +### supportsInterface + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#supportsinterface) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `supportsInterface(bytes4)` +- Function selector: `0x01ffc9a7` + +::: + +```solidity +function supportsInterface(bytes4 interfaceId) external view returns (bool); +``` + +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. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `interfaceId` | `bytes4` | - | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `bool` | - | + +
+ +### totalSupply + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#totalsupply) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `totalSupply()` +- Function selector: `0x18160ddd` + +::: + +```solidity +function totalSupply() external view returns (uint256); +``` + +Returns the number of existing tokens that have been minted in this contract. + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `uint256` | The number of existing tokens. | + +
+ +### transfer + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#transfer) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `transfer(address,address,uint256,bool,bytes)` +- Function selector: `0x760d9bba` + +::: + +```solidity +function transfer( + address from, + address to, + uint256 amount, + bool allowNonLSP1Recipient, + bytes data +) external nonpayable; +``` + +Transfers an `amount` of tokens from the `from` address to the `to` address and notify both sender and recipients via the LSP1 [`universalReceiver(...)`](#`universalreceiver) function. If the tokens are transferred by an operator on behalf of a token holder, the allowance for the operator will be decreased by `amount` once the token transfer has been completed (See [`authorizedAmountFor`](#authorizedamountfor)). + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address` | The sender address. | + +| `to` | `address` | The recipient address. | + +| `amount` | `uint256` | The amount of tokens to transfer. | + +| `allowNonLSP1Recipient` | `bool` | When set to `true`, the `to` address CAN be any address. When set to `false`, the `to` address MUST be a contract that supports the LSP1 UniversalReceiver standard. | + +| `data` | `bytes` | Any additional data the caller wants included in the emitted event, and sent in the hooks of the `from` and `to` addresses. | + +
+ +### transferBatch + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#transferbatch) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `transferBatch(address[],address[],uint256[],bool[],bytes[])` +- Function selector: `0x2d7667c9` + +::: + +```solidity +function transferBatch( + address[] from, + address[] to, + uint256[] amount, + bool[] allowNonLSP1Recipient, + bytes[] data +) external nonpayable; +``` + +Same as [`transfer(...)`](#`transfer) but transfer multiple tokens based on the arrays of `from`, `to`, `amount`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address[]` | An array of sending addresses. | + +| `to` | `address[]` | An array of receiving addresses. | + +| `amount` | `uint256[]` | An array of amount of tokens to transfer for each `from -> to` transfer. | + +| `allowNonLSP1Recipient` | `bool[]` | For each transfer, when set to `true`, the `to` address CAN be any address. When set to `false`, the `to` address MUST be a contract that supports the LSP1 UniversalReceiver standard. | + +| `data` | `bytes[]` | An array of additional data the caller wants included in the emitted event, and sent in the hooks to `from` and `to` addresses. | + +
+ +### transferOwnership + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#transferownership) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Function signature: `transferOwnership(address)` +- Function selector: `0xf2fde38b` + +::: + +```solidity +function transferOwnership(address newOwner) external nonpayable; +``` + +Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `newOwner` | `address` | - | + +
+ +## Internal Methods + +Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. + +Internal functions cannot be called externally, whether from other smart contracts, dApp interfaces, or backend services. Their restricted accessibility ensures that they remain exclusively available within the context of the current contract, promoting controlled and encapsulated usage of these internal utilities. + +### \_checkOwner + +```solidity +function _checkOwner() internal view; +``` + +Throws if the sender is not the owner. + +
+ +### \_setOwner + +```solidity +function _setOwner(address newOwner) internal nonpayable; +``` + +Changes the owner if `newOwner` and oldOwner are different +This pattern is useful in inheritance. + +
+ +### \_getData + +```solidity +function _getData(bytes32 dataKey) internal view returns (bytes dataValue); +``` + +
+ +### \_setData + +```solidity +function _setData(bytes32 dataKey, bytes dataValue) internal nonpayable; +``` + +_This function override the \_setData function to make the non-transferable flag not editable_ + +the ERC725Y data key `LSP7NonTransferable` cannot be changed +via this function once the digital asset contract has been deployed. + +
+ +### \_updateOperator + +```solidity +function _updateOperator( + address tokenOwner, + address operator, + uint256 amount +) internal nonpayable; +``` + +Changes token `amount` the `operator` has access to from `tokenOwner` tokens. +If the amount is zero then the operator is being revoked, otherwise the operator amount is being modified. + +
+ +### \_mint + +```solidity +function _mint( + address to, + uint256 amount, + bool allowNonLSP1Recipient, + bytes data +) internal nonpayable; +``` + +Mints `amount` of tokens and transfers it to `to`. + +
+ +**Emitted events:** + +- [`Transfer`](#transfer) event with `address(0)` as `from`. + +
+ +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `to` | `address` | the address to mint tokens for. | + +| `amount` | `uint256` | the amount of tokens to mint. | + +| `allowNonLSP1Recipient` | `bool` | a boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | + +| `data` | `bytes` | Additional data the caller wants included in the emitted {Transfer} event, and sent in the LSP1 hook to the `to` address. | + +
+ +### \_burn + +:::tip Hint + +In dApps, you can know which address is burning tokens by listening for the `Transfer` event and filter with the zero address as `to`. + +::: + +```solidity +function _burn(address from, uint256 amount, bytes data) internal nonpayable; +``` + +Burns (= destroys) `amount` of tokens, decrease the `from` balance. This is done by sending them to the zero address. +Both the sender and recipient will be notified of the token transfer through the LSP1 [`universalReceiver`](#universalreceiver) +function, if they are contracts that support the LSP1 interface. Their `universalReceiver` function will receive +all the parameters in the calldata packed encoded. +Any logic in the [`_beforeTokenTransfer`](#_beforetokentransfer) function will run before updating the balances. + +
+ +**Emitted events:** + +- [`Transfer`](#transfer) event with `address(0)` as the `to` address + +
+ +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address` | the address to burn tokens from its balance. | + +| `amount` | `uint256` | the amount of tokens to burn. | + +| `data` | `bytes` | Additional data the caller wants included in the emitted event, and sent in the LSP1 hook to the `from` and `to` address. | + +
+ +### \_transfer + +```solidity +function _transfer(address, address, uint256, bool, bytes) internal nonpayable; +``` + +This function override the internal `_transfer` function to make it non-transferable + +
+ +### \_beforeTokenTransfer + +```solidity +function _beforeTokenTransfer( + address from, + address to, + uint256 amount +) internal nonpayable; +``` + +Hook that is called before any token transfer, including minting and burning. +Allows to run custom logic before updating balances and notifiying sender/recipient by overriding this function. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address` | The sender address | + +| `to` | `address` | The recipient address | + +| `amount` | `uint256` | The amount of token to transfer | + +
+ +### \_notifyTokenSender + +```solidity +function _notifyTokenSender(address from, bytes lsp1Data) internal nonpayable; +``` + +Attempt to notify the token sender `from` about the `amount` of tokens being transferred. +This is done by calling its [`universalReceiver`](#universalreceiver) function with the `_TYPEID_LSP7_TOKENSSENDER` as typeId, if `from` is a contract that supports the LSP1 interface. +If `from` is an EOA or a contract that does not support the LSP1 interface, nothing will happen and no notification will be sent. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address` | The address to call the {universalReceiver} function on. | + +| `lsp1Data` | `bytes` | the data to be sent to the `from` address in the `universalReceiver` call. | + +
+ +### \_notifyTokenReceiver + +```solidity +function _notifyTokenReceiver( + address to, + bool allowNonLSP1Recipient, + bytes lsp1Data +) internal nonpayable; +``` + +Attempt to notify the token receiver `to` about the `amount` tokens being received. +This is done by calling its [`universalReceiver`](#universalreceiver) function with the `_TYPEID_LSP7_TOKENSRECIPIENT` as typeId, if `to` is a contract that supports the LSP1 interface. +If `to` is is an EOA or a contract that does not support the LSP1 interface, the behaviour will depend on the `allowNonLSP1Recipient` boolean flag. + +- if `allowNonLSP1Recipient` is set to `true`, nothing will happen and no notification will be sent. + +- if `allowNonLSP1Recipient` is set to `false, the transaction will revert. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `to` | `address` | The address to call the {universalReceiver} function on. | + +| `allowNonLSP1Recipient` | `bool` | a boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | + +| `lsp1Data` | `bytes` | the data to be sent to the `to` address in the `universalReceiver(...)` call. | + +
+ +## Events + +### AuthorizedOperator + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#authorizedoperator) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Event signature: `AuthorizedOperator(address,address,uint256)` +- Event topic hash: `0xd66aff874162a96578e919097b6f6d153dfd89a5cec41bb331fdb0c4aec16e2c` + +::: + +```solidity +event AuthorizedOperator(address indexed operator, address indexed tokenOwner, uint256 indexed amount); +``` + +Emitted when `tokenOwner` enables `operator` to transfer or burn the `tokenId`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` **`indexed`** | `address` | The address authorized as an operator. | + +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | + +| `amount` **`indexed`** | `uint256` | The amount of tokens `operator` address has access to from `tokenOwner` | + +
+ +### DataChanged + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#datachanged) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Event signature: `DataChanged(bytes32,bytes)` +- Event topic hash: `0xece574603820d07bc9b91f2a932baadf4628aabcb8afba49776529c14a6104b2` + +::: + +```solidity +event DataChanged(bytes32 indexed dataKey, bytes dataValue); +``` + +_Emitted when data at a key is changed_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKey` **`indexed`** | `bytes32` | The data key which data value is set | + +| `dataValue` | `bytes` | The data value to set | + +
+ +### OwnershipTransferred + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#ownershiptransferred) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Event signature: `OwnershipTransferred(address,address)` +- Event topic hash: `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` + +::: + +```solidity +event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); +``` + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `previousOwner` **`indexed`** | `address` | - | + +| `newOwner` **`indexed`** | `address` | - | + +
+ +### RevokedOperator + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokedoperator) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Event signature: `RevokedOperator(address,address)` +- Event topic hash: `0x50546e66e5f44d728365dc3908c63bc5cfeeab470722c1677e3073a6ac294aa1` + +::: + +```solidity +event RevokedOperator(address indexed operator, address indexed tokenOwner); +``` + +Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | + +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | + +
+ +### Transfer + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#transfer) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Event signature: `Transfer(address,address,address,uint256,bool,bytes)` +- Event topic hash: `0x3997e418d2cef0b3b0e907b1e39605c3f7d32dbd061e82ea5b4a770d46a160a6` + +::: + +```solidity +event Transfer(address indexed operator, address indexed from, address indexed to, uint256 amount, bool allowNonLSP1Recipient, bytes data); +``` + +Emitted when `tokenId` token is transferred from the `from` to the `to` address. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` **`indexed`** | `address` | The address of operator that sent the `tokenId` | + +| `from` **`indexed`** | `address` | The previous owner of the `tokenId` | + +| `to` **`indexed`** | `address` | The new owner of `tokenId` | + +| `amount` | `uint256` | The amount of tokens transferred. | + +| `allowNonLSP1Recipient` | `bool` | If the token transfer enforces the `to` recipient address to be a contract that implements the LSP1 standard or not. | + +| `data` | `bytes` | Any additional data the caller included by the caller during the transfer, and sent in the hooks to the `from` and `to` addresses. | + +
+ +## Errors + +### ERC725Y_DataKeysValuesEmptyArray + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#erc725y_datakeysvaluesemptyarray) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `ERC725Y_DataKeysValuesEmptyArray()` +- Error hash: `0x97da5f95` + +::: + +```solidity +error ERC725Y_DataKeysValuesEmptyArray(); +``` + +reverts when one of the array parameter provided to `setDataBatch` is an empty array + +
+ +### ERC725Y_DataKeysValuesLengthMismatch + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#erc725y_datakeysvalueslengthmismatch) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `ERC725Y_DataKeysValuesLengthMismatch()` +- Error hash: `0x3bcc8979` + +::: + +```solidity +error ERC725Y_DataKeysValuesLengthMismatch(); +``` + +reverts when there is not the same number of elements in the lists of data keys and data values when calling setDataBatch. + +
+ +### ERC725Y_MsgValueDisallowed + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#erc725y_msgvaluedisallowed) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `ERC725Y_MsgValueDisallowed()` +- Error hash: `0xf36ba737` + +::: + +```solidity +error ERC725Y_MsgValueDisallowed(); +``` + +reverts when sending value to the `setData(..)` functions + +
+ +### LSP4TokenNameNotEditable + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp4tokennamenoteditable) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP4TokenNameNotEditable()` +- Error hash: `0x85c169bd` + +::: + +```solidity +error LSP4TokenNameNotEditable(); +``` + +Reverts when trying to edit the data key `LSP4TokenName` after the digital asset contract has been deployed. The `LSP4TokenName` data key is located inside the ERC725Y Data key-value store of the digital asset contract. It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + +
+ +### LSP4TokenSymbolNotEditable + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp4tokensymbolnoteditable) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP4TokenSymbolNotEditable()` +- Error hash: `0x76755b38` + +::: + +```solidity +error LSP4TokenSymbolNotEditable(); +``` + +Reverts when trying to edit the data key `LSP4TokenSymbol` after the digital asset contract has been deployed. The `LSP4TokenSymbol` data key is located inside the ERC725Y Data key-value store of the digital asset contract. It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + +
+ +### LSP7AmountExceedsAuthorizedAmount + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7amountexceedsauthorizedamount) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP7AmountExceedsAuthorizedAmount(address,uint256,address,uint256)` +- Error hash: `0xf3a6b691` + +::: + +```solidity +error LSP7AmountExceedsAuthorizedAmount( + address tokenOwner, + uint256 authorizedAmount, + address operator, + uint256 amount +); +``` + +reverts when `operator` of `tokenOwner` send an `amount` of tokens larger than the `authorizedAmount`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenOwner` | `address` | - | + +| `authorizedAmount` | `uint256` | - | + +| `operator` | `address` | - | + +| `amount` | `uint256` | - | + +
+ +### LSP7CannotSendToSelf + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7cannotsendtoself) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP7CannotSendToSelf()` +- Error hash: `0xb9afb000` + +::: + +```solidity +error LSP7CannotSendToSelf(); +``` + +reverts when specifying the same address for `from` or `to` in a token transfer. + +
+ +### LSP7CannotUseAddressZeroAsOperator + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7cannotuseaddresszeroasoperator) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP7CannotUseAddressZeroAsOperator()` +- Error hash: `0x6355e766` + +::: + +```solidity +error LSP7CannotUseAddressZeroAsOperator(); +``` + +reverts when trying to set the zero address as an operator. + +
+ +### LSP7DecreasedAllowanceBelowZero + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7decreasedallowancebelowzero) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP7DecreasedAllowanceBelowZero()` +- Error hash: `0x0ef76c35` + +::: + +```solidity +error LSP7DecreasedAllowanceBelowZero(); +``` + +Reverts when trying to decrease an operator's allowance to more than its current allowance. + +
+ +### LSP7InvalidTransferBatch + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7invalidtransferbatch) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP7InvalidTransferBatch()` +- Error hash: `0x263eee8d` + +::: + +```solidity +error LSP7InvalidTransferBatch(); +``` + +reverts when the array parameters used in [`transferBatch`](#transferbatch) have different lengths. + +
+ +### LSP7NonTransferableNotEditable + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7nontransferablenoteditable) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP7NonTransferableNotEditable()` +- Error hash: `0x3dfed5a8` + +::: + +```solidity +error LSP7NonTransferableNotEditable(); +``` + +Reverts when trying to edit the data key `LSP7NonTransferable` after the digital asset contract has been deployed. The `LSP7NonTransferable` data key is located inside the ERC725Y Data key-value store of the digital asset contract. It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + +
+ +### LSP7TokenOwnerCannotBeOperator + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#lsp7tokenownercannotbeoperator) +- Solidity implementation: [`LSP7NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol) +- Error signature: `LSP7TokenOwnerCannotBeOperator()` +- Error hash: `0xdab75047` + +::: + +```solidity +error LSP7TokenOwnerCannotBeOperator(); +``` + +reverts when trying to authorize or revoke the token's owner as an operator. + +
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.md new file mode 100644 index 000000000..b3a7ba6ee --- /dev/null +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.md @@ -0,0 +1,1343 @@ + + + +# LSP8NonTransferable + +:::info Standard Specifications + +[`LSP-8-IdentifiableDigitalAsset`](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md) + +::: +:::info Solidity implementation + +[`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) + +::: + +LSP8 extension, adds the concept of a non-transferable token. + +## Public Methods + +Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts. +When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally. + +### constructor + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#constructor) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) + +::: + +```solidity +constructor(string name_, string symbol_, address newOwner_); +``` + +_Deploying a `LSP8NonTransferable` token contract with: token name = `name_`, token symbol = `symbol_`, address `newOwner_` as the token contract owner. Switch ON the non-transferable flag._ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `name_` | `string` | The name of the token. | + +| `symbol_` | `string` | The symbol of the token. | + +| `newOwner_` | `address` | The owner of the token contract. | + +
+ +### authorizeOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#authorizeoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `authorizeOperator(address,bytes32)` +- Function selector: `0xcf5182ba` + +::: + +```solidity +function authorizeOperator( + address operator, + bytes32 tokenId +) external nonpayable; +``` + +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | The address to authorize as an operator. | + +| `tokenId` | `bytes32` | The token ID operator has access to.. | + +
+ +### balanceOf + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#balanceof) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `balanceOf(address)` +- Function selector: `0x70a08231` + +::: + +```solidity +function balanceOf(address tokenOwner) external view returns (uint256); +``` + +Get the number of token IDs owned by `tokenOwner`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenOwner` | `address` | The address to query \* | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `uint256` | The total number of token IDs that `tokenOwner` owns. | + +
+ +### getData + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#getdata) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `getData(bytes32)` +- Function selector: `0x54f6127f` + +::: + +```solidity +function getData(bytes32 dataKey) external view returns (bytes dataValue); +``` + +_Gets singular data at a given `dataKey`_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKey` | `bytes32` | The key which value to retrieve | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataValue` | `bytes` | The data stored at the key | + +
+ +### getDataBatch + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#getdatabatch) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `getDataBatch(bytes32[])` +- Function selector: `0xdedff9c6` + +::: + +```solidity +function getDataBatch( + bytes32[] dataKeys +) external view returns (bytes[] dataValues); +``` + +_Gets array of data for multiple given keys_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKeys` | `bytes32[]` | The array of keys which values to retrieve | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataValues` | `bytes[]` | The array of data stored at multiple keys | + +
+ +### getOperatorsOf + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#getoperatorsof) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `getOperatorsOf(bytes32)` +- Function selector: `0x49a6078d` + +::: + +```solidity +function getOperatorsOf(bytes32 tokenId) external view returns (address[]); +``` + +Returns all `operator` addresses that are allowed to transfer or burn a specific `tokenId` on behalf of its owner. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenId` | `bytes32` | The token ID to get the operators for. | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `address[]` | An array of operators allowed to transfer or burn a specific `tokenId`. Requirements - `tokenId` must exist. | + +
+ +### isOperatorFor + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#isoperatorfor) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `isOperatorFor(address,bytes32)` +- Function selector: `0x2a3654a4` + +::: + +```solidity +function isOperatorFor( + address operator, + bytes32 tokenId +) external view returns (bool); +``` + +Returns whether `operator` address is an operator for a given `tokenId`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | The address to query operator status for. | + +| `tokenId` | `bytes32` | The token ID to check if `operator` is allowed to operate on. | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `bool` | `true` if `operator` is an operator for `tokenId`, `false` otherwise. | + +
+ +### owner + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#owner) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `owner()` +- Function selector: `0x8da5cb5b` + +::: + +```solidity +function owner() external view returns (address); +``` + +Returns the address of the current owner. + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `address` | - | + +
+ +### renounceOwnership + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#renounceownership) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `renounceOwnership()` +- Function selector: `0x715018a6` + +::: + +```solidity +function renounceOwnership() external nonpayable; +``` + +Leaves the contract without owner. It will not be possible to call `onlyOwner` functions anymore. Can only be called by the current owner. NOTE: Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. + +
+ +### revokeOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `revokeOperator(address,bytes32)` +- Function selector: `0x0b0c6d82` + +::: + +```solidity +function revokeOperator(address operator, bytes32 tokenId) external nonpayable; +``` + +Remove access of `operator` for a given `tokenId`, disallowing it to transfer `tokenId` on behalf of its owner. See also [`isOperatorFor`](#isoperatorfor). + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | The address to revoke as an operator. | + +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | + +
+ +### setData + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#setdata) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `setData(bytes32,bytes)` +- Function selector: `0x7f23690c` + +::: + +```solidity +function setData(bytes32 dataKey, bytes dataValue) external payable; +``` + +_Sets singular data for a given `dataKey`_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKey` | `bytes32` | The key to retrieve stored value | + +| `dataValue` | `bytes` | The value to set SHOULD only be callable by the owner of the contract set via ERC173 The function is marked as payable to enable flexibility on child contracts If the function is not intended to receive value, an additional check should be implemented to check that value equal 0. Emits a {DataChanged} event. | + +
+ +### setDataBatch + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#setdatabatch) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `setDataBatch(bytes32[],bytes[])` +- Function selector: `0x97902421` + +::: + +```solidity +function setDataBatch(bytes32[] dataKeys, bytes[] dataValues) external payable; +``` + +Sets array of data for multiple given `dataKeys` SHOULD only be callable by the owner of the contract set via ERC173 The function is marked as payable to enable flexibility on child contracts If the function is not intended to receive value, an additional check should be implemented to check that value equal + +0. Emits a [`DataChanged`](#datachanged) event. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKeys` | `bytes32[]` | The array of data keys for values to set | + +| `dataValues` | `bytes[]` | The array of values to set | + +
+ +### supportsInterface + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#supportsinterface) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `supportsInterface(bytes4)` +- Function selector: `0x01ffc9a7` + +::: + +```solidity +function supportsInterface(bytes4 interfaceId) external view returns (bool); +``` + +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. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `interfaceId` | `bytes4` | - | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `bool` | - | + +
+ +### tokenIdsOf + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#tokenidsof) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `tokenIdsOf(address)` +- Function selector: `0xa3b261f2` + +::: + +```solidity +function tokenIdsOf(address tokenOwner) external view returns (bytes32[]); +``` + +Returns the list of token IDs that the `tokenOwner` address owns. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenOwner` | `address` | The address that we want to get the list of token IDs for. | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `bytes32[]` | An array of `bytes32[] tokenIds` owned by `tokenOwner`. | + +
+ +### tokenOwnerOf + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#tokenownerof) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `tokenOwnerOf(bytes32)` +- Function selector: `0x217b2270` + +::: + +```solidity +function tokenOwnerOf(bytes32 tokenId) external view returns (address); +``` + +Returns the list of `tokenIds` for the `tokenOwner` address. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenId` | `bytes32` | tokenOwner The address to query owned tokens | + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `address` | The owner address of the given `tokenId`. | + +
+ +### totalSupply + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#totalsupply) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `totalSupply()` +- Function selector: `0x18160ddd` + +::: + +```solidity +function totalSupply() external view returns (uint256); +``` + +Returns the number of existing tokens that have been minted in this contract. + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `uint256` | The number of existing tokens. | + +
+ +### transfer + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#transfer) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `transfer(address,address,bytes32,bool,bytes)` +- Function selector: `0x511b6952` + +::: + +```solidity +function transfer( + address from, + address to, + bytes32 tokenId, + bool allowNonLSP1Recipient, + bytes data +) external nonpayable; +``` + +Transfer a given `tokenId` token from the `from` address to the `to` address. If operators are set for a specific `tokenId`, all the operators are revoked after the tokenId have been transferred. The `allowNonLSP1Recipient` parameter MUST be set to `true` when transferring tokens to Externally Owned Accounts (EOAs) or contracts that do not implement the LSP1 standard. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address` | The address that owns the given `tokenId`. | + +| `to` | `address` | The address that will receive the `tokenId`. | + +| `tokenId` | `bytes32` | The token ID to transfer. | + +| `allowNonLSP1Recipient` | `bool` | When set to `true`, the `to` address CAN be any addres. When set to `false`, the `to` address MUST be a contract that supports the LSP1 UniversalReceiver standard. | + +| `data` | `bytes` | Any additional data the caller wants included in the emitted event, and sent in the hooks of the `from` and `to` addresses. | + +
+ +### transferBatch + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#transferbatch) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `transferBatch(address[],address[],bytes32[],bool[],bytes[])` +- Function selector: `0x7e87632c` + +::: + +```solidity +function transferBatch( + address[] from, + address[] to, + bytes32[] tokenId, + bool[] allowNonLSP1Recipient, + bytes[] data +) external nonpayable; +``` + +Transfers multiple tokens at once based on the arrays of `from`, `to` and `tokenId`. If any transfer fails, the whole call will revert. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address[]` | An array of sending addresses. | + +| `to` | `address[]` | An array of recipient addresses. | + +| `tokenId` | `bytes32[]` | An array of token IDs to transfer. | + +| `allowNonLSP1Recipient` | `bool[]` | When set to `true`, `to` may be any address. When set to `false`, `to` must be a contract that supports the LSP1 standard and not revert. | + +| `data` | `bytes[]` | Any additional data the caller wants included in the emitted event, and sent in the hooks to the `from` and `to` addresses. | + +
+ +### transferOwnership + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#transferownership) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Function signature: `transferOwnership(address)` +- Function selector: `0xf2fde38b` + +::: + +```solidity +function transferOwnership(address newOwner) external nonpayable; +``` + +Transfers ownership of the contract to a new account (`newOwner`). Can only be called by the current owner. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `newOwner` | `address` | - | + +
+ +## Internal Methods + +Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. + +Internal functions cannot be called externally, whether from other smart contracts, dApp interfaces, or backend services. Their restricted accessibility ensures that they remain exclusively available within the context of the current contract, promoting controlled and encapsulated usage of these internal utilities. + +### \_checkOwner + +```solidity +function _checkOwner() internal view; +``` + +Throws if the sender is not the owner. + +
+ +### \_setOwner + +```solidity +function _setOwner(address newOwner) internal nonpayable; +``` + +Changes the owner if `newOwner` and oldOwner are different +This pattern is useful in inheritance. + +
+ +### \_getData + +```solidity +function _getData(bytes32 dataKey) internal view returns (bytes dataValue); +``` + +
+ +### \_setData + +```solidity +function _setData(bytes32 dataKey, bytes dataValue) internal nonpayable; +``` + +_This function override the \_setData function to make the non-transferable flag not editable_ + +the ERC725Y data key `LSP8NonTransferable` cannot be changed +via this function once the digital asset contract has been deployed. + +
+ +### \_isOperatorOrOwner + +```solidity +function _isOperatorOrOwner( + address caller, + bytes32 tokenId +) internal view returns (bool); +``` + +verifies if the `caller` is operator or owner for the `tokenId` + +#### Returns + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `0` | `bool` | true if `caller` is either operator or owner | + +
+ +### \_revokeOperator + +```solidity +function _revokeOperator( + address operator, + address tokenOwner, + bytes32 tokenId +) internal nonpayable; +``` + +removes `operator` from the list of operators for the `tokenId` + +
+ +### \_clearOperators + +```solidity +function _clearOperators( + address tokenOwner, + bytes32 tokenId +) internal nonpayable; +``` + +revoke all the current operators for a specific `tokenId` token which belongs to `tokenOwner`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenOwner` | `address` | The address that is the owner of the `tokenId`. | + +| `tokenId` | `bytes32` | The token to remove the associated operators for. | + +
+ +### \_exists + +```solidity +function _exists(bytes32 tokenId) internal view returns (bool); +``` + +Returns whether `tokenId` exists. +Tokens start existing when they are minted ([`_mint`](#_mint)), and stop existing when they are burned ([`_burn`](#_burn)). + +
+ +### \_existsOrError + +```solidity +function _existsOrError(bytes32 tokenId) internal view; +``` + +When `tokenId` does not exist then revert with an error. + +
+ +### \_mint + +```solidity +function _mint( + address to, + bytes32 tokenId, + bool allowNonLSP1Recipient, + bytes data +) internal nonpayable; +``` + +Create `tokenId` by minting it and transfers it to `to`. + +
+ +**Emitted events:** + +- [`Transfer`](#transfer) event with `address(0)` as `from` address. + +
+ +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `to` | `address` | @param tokenId The token ID to create (= mint). | + +| `tokenId` | `bytes32` | The token ID to create (= mint). | + +| `allowNonLSP1Recipient` | `bool` | When set to `true`, `to` may be any address. When set to `false`, `to` must be a contract that supports the LSP1 standard. | + +| `data` | `bytes` | Any additional data the caller wants included in the emitted event, and sent in the hook of the `to` address. | + +
+ +### \_burn + +:::tip Hint + +In dApps, you can know which addresses are burning tokens by listening for the `Transfer` event and filter with the zero address as `to`. + +::: + +```solidity +function _burn(bytes32 tokenId, bytes data) internal nonpayable; +``` + +Burn a specific `tokenId`, removing the `tokenId` from the [`tokenIdsOf`](#tokenidsof) the caller and decreasing its [`balanceOf`](#balanceof) by -1. +This will also clear all the operators allowed to transfer the `tokenId`. +The owner of the `tokenId` will be notified about the `tokenId` being transferred through its LSP1 [`universalReceiver`](#universalreceiver) +function, if it is a contract that supports the LSP1 interface. Its [`universalReceiver`](#universalreceiver) function will receive +all the parameters in the calldata packed encoded. +Any logic in the [`_beforeTokenTransfer`](#_beforetokentransfer) function will run before burning `tokenId` and updating the balances. + +
+ +**Emitted events:** + +- [`Transfer`](#transfer) event with `address(0)` as the `to` address. + +
+ +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenId` | `bytes32` | The token to burn. | + +| `data` | `bytes` | Any additional data the caller wants included in the emitted event, and sent in the LSP1 hook on the token owner's address. | + +
+ +### \_transfer + +```solidity +function _transfer(address, address, bytes32, bool, bytes) internal nonpayable; +``` + +This function override the internal `_transfer` function to make it non-transferable + +
+ +### \_beforeTokenTransfer + +```solidity +function _beforeTokenTransfer( + address from, + address to, + bytes32 tokenId +) internal nonpayable; +``` + +Hook that is called before any token transfer, including minting and burning. + +- Allows to run custom logic before updating balances and notifiying sender/recipient by overriding this function. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `from` | `address` | The sender address | + +| `to` | `address` | @param tokenId The tokenId to transfer | + +| `tokenId` | `bytes32` | The tokenId to transfer | + +
+ +### \_notifyTokenSender + +```solidity +function _notifyTokenSender(address from, bytes lsp1Data) internal nonpayable; +``` + +An attempt is made to notify the token sender about the `tokenId` changing owners using +LSP1 interface. + +
+ +### \_notifyTokenReceiver + +```solidity +function _notifyTokenReceiver( + address to, + bool allowNonLSP1Recipient, + bytes lsp1Data +) internal nonpayable; +``` + +An attempt is made to notify the token receiver about the `tokenId` changing owners +using LSP1 interface. When allowNonLSP1Recipient is FALSE the token receiver MUST support LSP1. +The receiver may revert when the token being sent is not wanted. + +
+ +## Events + +### AuthorizedOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#authorizedoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Event signature: `AuthorizedOperator(address,address,bytes32)` +- Event topic hash: `0x34b797fc5a526f7bf1d2b5de25f6564fd85ae364e3ee939aee7c1ac27871a988` + +::: + +```solidity +event AuthorizedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId); +``` + +Emitted when `tokenOwner` enables `operator` to transfer or burn the `tokenId`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` **`indexed`** | `address` | The address authorized as an operator. | + +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | + +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` address has access on behalf of `tokenOwner`. | + +
+ +### DataChanged + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#datachanged) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Event signature: `DataChanged(bytes32,bytes)` +- Event topic hash: `0xece574603820d07bc9b91f2a932baadf4628aabcb8afba49776529c14a6104b2` + +::: + +```solidity +event DataChanged(bytes32 indexed dataKey, bytes dataValue); +``` + +_Emitted when data at a key is changed_ + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `dataKey` **`indexed`** | `bytes32` | The data key which data value is set | + +| `dataValue` | `bytes` | The data value to set | + +
+ +### OwnershipTransferred + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#ownershiptransferred) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Event signature: `OwnershipTransferred(address,address)` +- Event topic hash: `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` + +::: + +```solidity +event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); +``` + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `previousOwner` **`indexed`** | `address` | - | + +| `newOwner` **`indexed`** | `address` | - | + +
+ +### RevokedOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Event signature: `RevokedOperator(address,address,bytes32)` +- Event topic hash: `0x17d5389f6ab6adb2647dfa0aa365c323d37adacc30b33a65310b6158ce1373d5` + +::: + +```solidity +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId); +``` + +Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | + +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | + +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | + +
+ +### Transfer + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#transfer) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Event signature: `Transfer(address,address,address,bytes32,bool,bytes)` +- Event topic hash: `0xb333c813a7426a7a11e2b190cad52c44119421594b47f6f32ace6d8c7207b2bf` + +::: + +```solidity +event Transfer(address operator, address indexed from, address indexed to, bytes32 indexed tokenId, bool allowNonLSP1Recipient, bytes data); +``` + +Emitted when `tokenId` token is transferred from the `from` to the `to` address. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | The address of operator that sent the `tokenId` | + +| `from` **`indexed`** | `address` | The previous owner of the `tokenId` | + +| `to` **`indexed`** | `address` | The new owner of `tokenId` | + +| `tokenId` **`indexed`** | `bytes32` | The tokenId that was transferred | + +| `allowNonLSP1Recipient` | `bool` | If the token transfer enforces the `to` recipient address to be a contract that implements the LSP1 standard or not. | + +| `data` | `bytes` | Any additional data the caller included by the caller during the transfer, and sent in the hooks to the `from` and `to` addresses. | + +
+ +## Errors + +### ERC725Y_DataKeysValuesEmptyArray + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#erc725y_datakeysvaluesemptyarray) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `ERC725Y_DataKeysValuesEmptyArray()` +- Error hash: `0x97da5f95` + +::: + +```solidity +error ERC725Y_DataKeysValuesEmptyArray(); +``` + +reverts when one of the array parameter provided to `setDataBatch` is an empty array + +
+ +### ERC725Y_DataKeysValuesLengthMismatch + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#erc725y_datakeysvalueslengthmismatch) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `ERC725Y_DataKeysValuesLengthMismatch()` +- Error hash: `0x3bcc8979` + +::: + +```solidity +error ERC725Y_DataKeysValuesLengthMismatch(); +``` + +reverts when there is not the same number of elements in the lists of data keys and data values when calling setDataBatch. + +
+ +### ERC725Y_MsgValueDisallowed + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#erc725y_msgvaluedisallowed) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `ERC725Y_MsgValueDisallowed()` +- Error hash: `0xf36ba737` + +::: + +```solidity +error ERC725Y_MsgValueDisallowed(); +``` + +reverts when sending value to the `setData(..)` functions + +
+ +### LSP4TokenNameNotEditable + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp4tokennamenoteditable) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP4TokenNameNotEditable()` +- Error hash: `0x85c169bd` + +::: + +```solidity +error LSP4TokenNameNotEditable(); +``` + +Reverts when trying to edit the data key `LSP4TokenName` after the digital asset contract has been deployed. The `LSP4TokenName` data key is located inside the ERC725Y Data key-value store of the digital asset contract. It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + +
+ +### LSP4TokenSymbolNotEditable + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp4tokensymbolnoteditable) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP4TokenSymbolNotEditable()` +- Error hash: `0x76755b38` + +::: + +```solidity +error LSP4TokenSymbolNotEditable(); +``` + +Reverts when trying to edit the data key `LSP4TokenSymbol` after the digital asset contract has been deployed. The `LSP4TokenSymbol` data key is located inside the ERC725Y Data key-value store of the digital asset contract. It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + +
+ +### LSP8CannotUseAddressZeroAsOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8cannotuseaddresszeroasoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8CannotUseAddressZeroAsOperator()` +- Error hash: `0x9577b8b3` + +::: + +```solidity +error LSP8CannotUseAddressZeroAsOperator(); +``` + +reverts when trying to set the zero address as an operator. + +
+ +### LSP8InvalidTransferBatch + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8invalidtransferbatch) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8InvalidTransferBatch()` +- Error hash: `0x93a83119` + +::: + +```solidity +error LSP8InvalidTransferBatch(); +``` + +reverts when the parameters used for `transferBatch` have different lengths. + +
+ +### LSP8NonExistentTokenId + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8nonexistenttokenid) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8NonExistentTokenId(bytes32)` +- Error hash: `0xae8f9a36` + +::: + +```solidity +error LSP8NonExistentTokenId(bytes32 tokenId); +``` + +reverts when `tokenId` has not been minted. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenId` | `bytes32` | - | + +
+ +### LSP8NonExistingOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8nonexistingoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8NonExistingOperator(address,bytes32)` +- Error hash: `0x4aa31a8c` + +::: + +```solidity +error LSP8NonExistingOperator(address operator, bytes32 tokenId); +``` + +reverts when `operator` is not an operator for the `tokenId`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | - | + +| `tokenId` | `bytes32` | - | + +
+ +### LSP8NonTransferableNotEditable + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8nontransferablenoteditable) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8NonTransferableNotEditable()` +- Error hash: `0xd34e8077` + +::: + +```solidity +error LSP8NonTransferableNotEditable(); +``` + +Reverts when trying to edit the data key `LSP8NonTransferable` after the digital asset contract has been deployed. The `LSP8NonTransferable` data key is located inside the ERC725Y Data key-value store of the digital asset contract. It can be set only once inside the constructor/initializer when the digital asset contract is being deployed. + +
+ +### LSP8NotTokenOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8nottokenoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8NotTokenOperator(bytes32,address)` +- Error hash: `0x1294d2a9` + +::: + +```solidity +error LSP8NotTokenOperator(bytes32 tokenId, address caller); +``` + +reverts when `caller` is not an allowed operator for `tokenId`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenId` | `bytes32` | - | + +| `caller` | `address` | - | + +
+ +### LSP8NotTokenOwner + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8nottokenowner) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8NotTokenOwner(address,bytes32,address)` +- Error hash: `0x5b271ea2` + +::: + +```solidity +error LSP8NotTokenOwner(address tokenOwner, bytes32 tokenId, address caller); +``` + +reverts when `caller` is not the `tokenOwner` of the `tokenId`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `tokenOwner` | `address` | - | + +| `tokenId` | `bytes32` | - | + +| `caller` | `address` | - | + +
+ +### LSP8OperatorAlreadyAuthorized + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8operatoralreadyauthorized) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8OperatorAlreadyAuthorized(address,bytes32)` +- Error hash: `0xa7626b68` + +::: + +```solidity +error LSP8OperatorAlreadyAuthorized(address operator, bytes32 tokenId); +``` + +reverts when `operator` is already authorized for the `tokenId`. + +#### Parameters + +| Name | Type | Description | +| ---- | :--: | ----------- | + +| `operator` | `address` | - | + +| `tokenId` | `bytes32` | - | + +
+ +### LSP8TokenOwnerCannotBeOperator + +:::note References + +- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#lsp8tokenownercannotbeoperator) +- Solidity implementation: [`LSP8NonTransferable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol) +- Error signature: `LSP8TokenOwnerCannotBeOperator()` +- Error hash: `0x89fdad62` + +::: + +```solidity +error LSP8TokenOwnerCannotBeOperator(); +``` + +reverts when trying to authorize or revoke the token's owner as an operator. + +
diff --git a/dodoc/config.ts b/dodoc/config.ts index ce8863884..6d4438c75 100644 --- a/dodoc/config.ts +++ b/dodoc/config.ts @@ -25,6 +25,7 @@ export const dodocConfig = { 'contracts/LSP7DigitalAsset/extensions/LSP7Burnable.sol', 'contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.sol', 'contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol', + 'contracts/LSP7DigitalAsset/extensions/LSP7NonTransferable.sol', 'contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.sol', 'contracts/LSP7DigitalAsset/presets/LSP7Mintable.sol', 'contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol', @@ -32,6 +33,7 @@ export const dodocConfig = { 'contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.sol', 'contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol', 'contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.sol', + 'contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8NonTransferable.sol', 'contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.sol', 'contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.sol', diff --git a/tests/LSP7DigitalAsset/LSP7NonTransferable.behaviour.ts b/tests/LSP7DigitalAsset/LSP7NonTransferable.behaviour.ts new file mode 100644 index 000000000..883d5f983 --- /dev/null +++ b/tests/LSP7DigitalAsset/LSP7NonTransferable.behaviour.ts @@ -0,0 +1,90 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { ethers } from 'hardhat'; +import { expect } from 'chai'; +import { LSP7NonTransferableTester } from '../../types'; + +import { ERC725YDataKeys } from '../../constants'; + +export type LSP7NonTransferableTestAccounts = { + owner: SignerWithAddress; + firstRecipient: SignerWithAddress; + secondRecipient: SignerWithAddress; +}; + +export const getNamedAccounts = async (): Promise => { + const [owner, firstRecipient, secondRecipient] = await ethers.getSigners(); + return { owner, firstRecipient, secondRecipient }; +}; + +export type LSP7NonTransferableDeployParams = { + name: string; + symbol: string; + newOwner: string; +}; + +export type LSP7NonTransferableTestContext = { + accounts: LSP7NonTransferableTestAccounts; + lsp7NonTransferable: LSP7NonTransferableTester; + deployParams: LSP7NonTransferableDeployParams; +}; + +export const shouldBehaveLikeLSP7NonTransferable = ( + buildContext: () => Promise, +) => { + let context: LSP7NonTransferableTestContext; + + before(async () => { + context = await buildContext(); + }); + + describe('when setting data on ERC725Y storage', () => { + it('should revert trying to edit lsp7NonTransferable key', async () => { + const nonTransferable = ERC725YDataKeys.LSP7.LSP7NonTransferable; + const value = '0x00'; + expect( + context.lsp7NonTransferable + .connect(context.deployParams.newOwner) + .setData(nonTransferable, value), + ).to.be.revertedWithCustomError( + context.lsp7NonTransferable, + 'LSP7NonTransferableNotEditable', + ); + }); + }); + + describe('when transferring a token', () => { + before(async () => { + await context.lsp7NonTransferable + .connect(context.accounts.owner) + .mint(context.accounts.owner.address, 10, true, '0x'); + }); + + it('should revert if try to transfer a token', async () => { + await expect( + context.lsp7NonTransferable + .connect(context.accounts.owner) + .transfer( + context.accounts.owner.address, + context.accounts.firstRecipient.address, + 4, + true, + '0x', + ), + ).to.be.revertedWith('LSP7: Token is non-transferable'); + }); + + it('should revert if try to transfer batch of tokens', async () => { + await expect( + context.lsp7NonTransferable + .connect(context.accounts.owner) + .transferBatch( + [context.accounts.owner.address, context.accounts.owner.address], + [context.accounts.firstRecipient.address, context.accounts.secondRecipient.address], + [2, 5], + [true, true], + ['0x', '0x'], + ), + ).to.be.revertedWith('LSP7: Token is non-transferable'); + }); + }); +}; diff --git a/tests/LSP7DigitalAsset/proxy/LSP7NonTransferableInit.test.ts b/tests/LSP7DigitalAsset/proxy/LSP7NonTransferableInit.test.ts new file mode 100644 index 000000000..880f374f3 --- /dev/null +++ b/tests/LSP7DigitalAsset/proxy/LSP7NonTransferableInit.test.ts @@ -0,0 +1,111 @@ +import { expect } from 'chai'; + +import { + LSP7NonTransferableInitTester, + LSP7NonTransferableInitTester__factory, +} from '../../../types'; + +import { shouldInitializeLikeLSP7 } from '../LSP7DigitalAsset.behaviour'; + +import { deployProxy } from '../../utils/fixtures'; +import { ERC725YDataKeys } from '../../../constants'; +import { ContractTransaction } from 'ethers'; +import { + getNamedAccounts, + LSP7NonTransferableTestAccounts, + shouldBehaveLikeLSP7NonTransferable, +} from '../LSP7NonTransferable.behaviour'; + +type LSP7NonTransferableInitTestContext = { + accounts: LSP7NonTransferableTestAccounts; + lsp7NonTransferable: LSP7NonTransferableInitTester; + deployParams: { + name: string; + symbol: string; + newOwner: string; + isNonDivisible: boolean; + }; +}; + +describe('LSP7NonTransferableInit with proxy', () => { + const buildTestContext = async () => { + const accounts = await getNamedAccounts(); + const deployParams = { + name: 'LSP7 Non-Transferable - deployed with constructor', + symbol: 'BRN', + newOwner: accounts.owner.address, + isNonDivisible: false, + }; + + const lsp7NonTransferableImplementation = await new LSP7NonTransferableInitTester__factory( + accounts.owner, + ).deploy(); + const lsp7NonTransferableProxy = await deployProxy( + lsp7NonTransferableImplementation.address, + accounts.owner, + ); + const lsp7NonTransferable = lsp7NonTransferableImplementation.attach(lsp7NonTransferableProxy); + + return { accounts, lsp7NonTransferable, deployParams }; + }; + + const initializeProxy = async (context: LSP7NonTransferableInitTestContext) => { + return context.lsp7NonTransferable.initialize( + context.deployParams.name, + context.deployParams.symbol, + context.deployParams.newOwner, + context.deployParams.isNonDivisible, + ); + }; + + describe('when deploying the contract as proxy', () => { + let context: LSP7NonTransferableInitTestContext; + + before(async () => { + context = await buildTestContext(); + }); + + describe('when initializing the contract', () => { + let initializeTransaction: ContractTransaction; + + shouldInitializeLikeLSP7(async () => { + const { lsp7NonTransferable: lsp7, deployParams } = context; + initializeTransaction = await initializeProxy(context); + + return { + lsp7, + deployParams, + initializeTransaction, + }; + }); + + it('should flag the contract as Non-Transferable', async () => { + const nonTransferable = ERC725YDataKeys.LSP7.LSP7NonTransferable; + const expectedNonTransferableValue = '0x01'; + await expect(initializeTransaction) + .to.emit(context.lsp7NonTransferable, 'DataChanged') + .withArgs(nonTransferable, expectedNonTransferableValue); + expect(await context.lsp7NonTransferable.getData(nonTransferable)).to.equal( + expectedNonTransferableValue, + ); + }); + }); + + describe('when calling initialize more than once', () => { + it('should revert', async () => { + await expect(initializeProxy(context)).to.be.revertedWith( + 'Initializable: contract is already initialized', + ); + }); + }); + + describe('when testing deployed contract', () => { + shouldBehaveLikeLSP7NonTransferable(() => + buildTestContext().then(async (context) => { + await initializeProxy(context); + return context; + }), + ); + }); + }); +}); diff --git a/tests/LSP7DigitalAsset/standard/LSP7NonTransferable.test.ts b/tests/LSP7DigitalAsset/standard/LSP7NonTransferable.test.ts new file mode 100644 index 000000000..52eb54c46 --- /dev/null +++ b/tests/LSP7DigitalAsset/standard/LSP7NonTransferable.test.ts @@ -0,0 +1,77 @@ +import { LSP7NonTransferableTester, LSP7NonTransferableTester__factory } from '../../../types'; + +import { shouldInitializeLikeLSP7 } from '../LSP7DigitalAsset.behaviour'; +import { ERC725YDataKeys } from '../../../constants'; +import { expect } from 'chai'; +import { ContractTransaction } from 'ethers'; +import { + getNamedAccounts, + LSP7NonTransferableTestAccounts, + shouldBehaveLikeLSP7NonTransferable, +} from '../LSP7NonTransferable.behaviour'; + +type LSP7NonTransferableTestContext = { + accounts: LSP7NonTransferableTestAccounts; + lsp7NonTransferable: LSP7NonTransferableTester; + deployParams: { + name: string; + symbol: string; + newOwner: string; + }; +}; + +describe('lsp7NonTransferable with constructor', () => { + const buildTestContext = async () => { + const accounts = await getNamedAccounts(); + const deployParams = { + name: 'LSP7 Non-Transferable - deployed with constructor', + symbol: 'NTT', + newOwner: accounts.owner.address, + isNonDivisible: false, + }; + + const lsp7NonTransferable = await new LSP7NonTransferableTester__factory(accounts.owner).deploy( + deployParams.name, + deployParams.symbol, + deployParams.newOwner, + deployParams.isNonDivisible, + ); + + return { accounts, lsp7NonTransferable, deployParams }; + }; + + describe('when deploying the contract', () => { + let context: LSP7NonTransferableTestContext; + let initializeTransaction: ContractTransaction; + + before(async () => { + context = await buildTestContext(); + }); + + shouldInitializeLikeLSP7(async () => { + const { lsp7NonTransferable: lsp7, deployParams } = context; + initializeTransaction = context.lsp7NonTransferable.deployTransaction; + + return { + lsp7, + deployParams, + initializeTransaction, + }; + }); + + it('should flag the contract as Non-Transferable', async () => { + const nonTransferable = ERC725YDataKeys.LSP7.LSP7NonTransferable; + const expectedNonTransferableValue = '0x01'; + await expect(initializeTransaction) + .to.emit(context.lsp7NonTransferable, 'DataChanged') + .withArgs(nonTransferable, expectedNonTransferableValue); + expect(await context.lsp7NonTransferable.getData(nonTransferable)).to.equal( + expectedNonTransferableValue, + ); + }); + }); + + describe('when testing deployed contract', () => { + shouldBehaveLikeLSP7NonTransferable(buildTestContext); + }); +}); diff --git a/tests/LSP8IdentifiableDigitalAsset/LSP8NonTransferable.behaviour.ts b/tests/LSP8IdentifiableDigitalAsset/LSP8NonTransferable.behaviour.ts new file mode 100644 index 000000000..d49a20709 --- /dev/null +++ b/tests/LSP8IdentifiableDigitalAsset/LSP8NonTransferable.behaviour.ts @@ -0,0 +1,93 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { ethers } from 'hardhat'; +import { expect } from 'chai'; +import { LSP8NonTransferableTester } from '../../types'; + +import { ERC725YDataKeys } from '../../constants'; + +export type LSP8NonTransferableTestAccounts = { + owner: SignerWithAddress; + tokenReceiver: SignerWithAddress; +}; + +export const getNamedAccounts = async (): Promise => { + const [owner, tokenReceiver] = await ethers.getSigners(); + return { owner, tokenReceiver }; +}; + +export type LSP8NonTransferableDeployParams = { + name: string; + symbol: string; + newOwner: string; +}; + +export type LSP8NonTransferableTestContext = { + accounts: LSP8NonTransferableTestAccounts; + lsp8NonTransferable: LSP8NonTransferableTester; + deployParams: LSP8NonTransferableDeployParams; +}; + +export const shouldBehaveLikeLSP8NonTransferable = ( + buildContext: () => Promise, +) => { + let context: LSP8NonTransferableTestContext; + + before(async () => { + context = await buildContext(); + }); + + describe('when setting data on ERC725Y storage', () => { + it('should revert trying to edit lsp8NonTransferable key', async () => { + const nonTransferable = ERC725YDataKeys.LSP8.LSP8NonTransferable; + const value = '0x00'; + expect( + context.lsp8NonTransferable + .connect(context.deployParams.newOwner) + .setData(nonTransferable, value), + ).to.be.revertedWithCustomError( + context.lsp8NonTransferable, + 'LSP8NonTransferableNotEditable', + ); + }); + }); + + describe('when transferring a token', () => { + const tokensIds = [ethers.utils.randomBytes(32), ethers.utils.randomBytes(32)]; + + before(async () => { + for (const tokenId of tokensIds) { + await context.lsp8NonTransferable + .connect(context.accounts.owner) + .mint(context.accounts.owner.address, tokenId, true, '0x'); + } + }); + + it('should revert if try to transfer a token', async () => { + await expect( + context.lsp8NonTransferable + .connect(context.accounts.owner) + .transfer( + context.accounts.owner.address, + context.accounts.tokenReceiver.address, + tokensIds[0], + true, + '0x', + ), + ).to.be.revertedWith('LSP8: Token is non-transferable'); + }); + + it('should revert if try to transfer batch of tokens', async () => { + await expect( + context.lsp8NonTransferable + .connect(context.accounts.owner) + .transferBatch( + [context.accounts.owner.address, context.accounts.owner.address], + [context.accounts.tokenReceiver.address, context.accounts.tokenReceiver.address], + tokensIds, + [true, true], + ['0x', '0x'], + ), + ).to.be.revertedWith('LSP8: Token is non-transferable'); + }); + }); +}; diff --git a/tests/LSP8IdentifiableDigitalAsset/proxy/LSP8NonTransferableInit.test.ts b/tests/LSP8IdentifiableDigitalAsset/proxy/LSP8NonTransferableInit.test.ts new file mode 100644 index 000000000..76534ebb1 --- /dev/null +++ b/tests/LSP8IdentifiableDigitalAsset/proxy/LSP8NonTransferableInit.test.ts @@ -0,0 +1,108 @@ +import { expect } from 'chai'; + +import { + LSP8NonTransferableInitTester, + LSP8NonTransferableInitTester__factory, +} from '../../../types'; + +import { shouldInitializeLikeLSP8 } from '../LSP8IdentifiableDigitalAsset.behaviour'; + +import { deployProxy } from '../../utils/fixtures'; +import { ERC725YDataKeys } from '../../../constants'; +import { ContractTransaction } from 'ethers'; +import { + getNamedAccounts, + LSP8NonTransferableTestAccounts, + shouldBehaveLikeLSP8NonTransferable, +} from '../LSP8NonTransferable.behaviour'; + +type LSP8NonTransferableInitTestContext = { + accounts: LSP8NonTransferableTestAccounts; + lsp8NonTransferable: LSP8NonTransferableInitTester; + deployParams: { + name: string; + symbol: string; + newOwner: string; + }; +}; + +describe('LSP8NonTransferableInit with proxy', () => { + const buildTestContext = async () => { + const accounts = await getNamedAccounts(); + const deployParams = { + name: 'LSP8 Non-Transferable - deployed with constructor', + symbol: 'BRN', + newOwner: accounts.owner.address, + }; + + const lsp8NonTransferableImplementation = await new LSP8NonTransferableInitTester__factory( + accounts.owner, + ).deploy(); + const lsp8NonTransferableProxy = await deployProxy( + lsp8NonTransferableImplementation.address, + accounts.owner, + ); + const lsp8NonTransferable = lsp8NonTransferableImplementation.attach(lsp8NonTransferableProxy); + + return { accounts, lsp8NonTransferable, deployParams }; + }; + + const initializeProxy = async (context: LSP8NonTransferableInitTestContext) => { + return context.lsp8NonTransferable.initialize( + context.deployParams.name, + context.deployParams.symbol, + context.deployParams.newOwner, + ); + }; + + describe('when deploying the contract as proxy', () => { + let context: LSP8NonTransferableInitTestContext; + + before(async () => { + context = await buildTestContext(); + }); + + describe('when initializing the contract', () => { + let initializeTransaction: ContractTransaction; + + shouldInitializeLikeLSP8(async () => { + const { lsp8NonTransferable: lsp8, deployParams } = context; + initializeTransaction = await initializeProxy(context); + + return { + lsp8, + deployParams, + initializeTransaction, + }; + }); + + it('should flag the contract as Non-Transferable', async () => { + const nonTransferable = ERC725YDataKeys.LSP8.LSP8NonTransferable; + const expectedNonTransferableValue = '0x01'; + await expect(initializeTransaction) + .to.emit(context.lsp8NonTransferable, 'DataChanged') + .withArgs(nonTransferable, expectedNonTransferableValue); + expect(await context.lsp8NonTransferable.getData(nonTransferable)).to.equal( + expectedNonTransferableValue, + ); + }); + }); + + describe('when calling initialize more than once', () => { + it('should revert', async () => { + await expect(initializeProxy(context)).to.be.revertedWith( + 'Initializable: contract is already initialized', + ); + }); + }); + + describe('when testing deployed contract', () => { + shouldBehaveLikeLSP8NonTransferable(() => + buildTestContext().then(async (context) => { + await initializeProxy(context); + return context; + }), + ); + }); + }); +}); diff --git a/tests/LSP8IdentifiableDigitalAsset/standard/LSP8NonTransferable.test.ts b/tests/LSP8IdentifiableDigitalAsset/standard/LSP8NonTransferable.test.ts new file mode 100644 index 000000000..c9a3c7b4d --- /dev/null +++ b/tests/LSP8IdentifiableDigitalAsset/standard/LSP8NonTransferable.test.ts @@ -0,0 +1,75 @@ +import { LSP8NonTransferableTester, LSP8NonTransferableTester__factory } from '../../../types'; + +import { shouldInitializeLikeLSP8 } from '../LSP8IdentifiableDigitalAsset.behaviour'; +import { ERC725YDataKeys } from '../../../constants'; +import { expect } from 'chai'; +import { ContractTransaction } from 'ethers'; +import { + getNamedAccounts, + LSP8NonTransferableTestAccounts, + shouldBehaveLikeLSP8NonTransferable, +} from '../LSP8NonTransferable.behaviour'; + +type LSP8NonTransferableTestContext = { + accounts: LSP8NonTransferableTestAccounts; + lsp8NonTransferable: LSP8NonTransferableTester; + deployParams: { + name: string; + symbol: string; + newOwner: string; + }; +}; + +describe('lsp8NonTransferable with constructor', () => { + const buildTestContext = async () => { + const accounts = await getNamedAccounts(); + const deployParams = { + name: 'LSP8 Non-Transferable - deployed with constructor', + symbol: 'NTT', + newOwner: accounts.owner.address, + }; + + const lsp8NonTransferable = await new LSP8NonTransferableTester__factory(accounts.owner).deploy( + deployParams.name, + deployParams.symbol, + deployParams.newOwner, + ); + + return { accounts, lsp8NonTransferable, deployParams }; + }; + + describe('when deploying the contract', () => { + let context: LSP8NonTransferableTestContext; + let initializeTransaction: ContractTransaction; + + before(async () => { + context = await buildTestContext(); + }); + + shouldInitializeLikeLSP8(async () => { + const { lsp8NonTransferable: lsp8, deployParams } = context; + initializeTransaction = context.lsp8NonTransferable.deployTransaction; + + return { + lsp8, + deployParams, + initializeTransaction, + }; + }); + + it('should flag the contract as Non-Transferable', async () => { + const nonTransferable = ERC725YDataKeys.LSP8.LSP8NonTransferable; + const expectedNonTransferableValue = '0x01'; + await expect(initializeTransaction) + .to.emit(context.lsp8NonTransferable, 'DataChanged') + .withArgs(nonTransferable, expectedNonTransferableValue); + expect(await context.lsp8NonTransferable.getData(nonTransferable)).to.equal( + expectedNonTransferableValue, + ); + }); + }); + + describe('when testing deployed contract', () => { + shouldBehaveLikeLSP8NonTransferable(buildTestContext); + }); +});