From 63c1a0fb68502e11d58f7582e4e849d2f24c4924 Mon Sep 17 00:00:00 2001 From: Yamen Merhi Date: Fri, 27 Oct 2023 16:24:22 +0300 Subject: [PATCH] =?UTF-8?q?refactor!:=20Optional=20LSP1=C2=A0Notification?= =?UTF-8?q?=20on=20`revokeOperator(..)`=20(#763)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor!: make notification optional in revokeOperator * test: adjust the tests accordingly * docs: generate docs * chore: add override keyword * chore: fix lsp7compatibleERC20 * refactor: include authorization status bool in lsp1 data in LSP8 * refactor!: allow increase allowance only when allowance > 0 * test: adjust the tests accoridngly * docs: generate docs * docs: generate docs --- constants.ts | 4 +- .../LSP7DigitalAsset/ILSP7DigitalAsset.sol | 60 +++++ contracts/LSP7DigitalAsset/LSP7Constants.sol | 2 +- .../LSP7DigitalAsset/LSP7DigitalAssetCore.sol | 206 ++++++++---------- contracts/LSP7DigitalAsset/LSP7Errors.sol | 5 + .../extensions/LSP7CompatibleERC20.sol | 9 +- .../LSP7CompatibleERC20InitAbstract.sol | 8 +- .../ILSP8IdentifiableDigitalAsset.sol | 5 + .../LSP8Constants.sol | 2 +- .../LSP8IdentifiableDigitalAssetCore.sol | 37 ++-- .../Tokens/TokenReceiverWithLSP1Revert.sol | 28 +++ docs/_interface_ids_table.mdx | 4 +- .../LSP7DigitalAsset/LSP7DigitalAsset.md | 123 +++++------ .../extensions/LSP7Burnable.md | 123 +++++------ .../extensions/LSP7CappedSupply.md | 123 +++++------ .../extensions/LSP7CompatibleERC20.md | 110 ++++------ .../presets/LSP7CompatibleERC20Mintable.md | 110 ++++------ .../LSP7DigitalAsset/presets/LSP7Mintable.md | 123 +++++------ .../LSP8IdentifiableDigitalAsset.md | 26 ++- .../extensions/LSP8Burnable.md | 26 ++- .../extensions/LSP8CappedSupply.md | 26 ++- .../extensions/LSP8CompatibleERC721.md | 26 ++- .../extensions/LSP8Enumerable.md | 26 ++- .../presets/LSP8CompatibleERC721Mintable.md | 26 ++- .../presets/LSP8Mintable.md | 26 ++- ...P1UniversalReceiverDelegateUP.behaviour.ts | 6 +- ...niversalReceiverDelegateVault.behaviour.ts | 6 +- .../LSP7CompatibleERC20.behaviour.ts | 49 +---- .../LSP7DigitalAsset.behaviour.ts | 149 +++++-------- .../LSP8CompatibleERC721.behaviour.ts | 25 +-- .../LSP8IdentifiableDigitalAsset.behaviour.ts | 107 ++++----- 31 files changed, 753 insertions(+), 853 deletions(-) create mode 100644 contracts/Mocks/Tokens/TokenReceiverWithLSP1Revert.sol diff --git a/constants.ts b/constants.ts index c7802d7a8..5d3ecfaf0 100644 --- a/constants.ts +++ b/constants.ts @@ -29,8 +29,8 @@ export const INTERFACE_IDS = { LSP1UniversalReceiver: '0x6bb56a14', LSP1UniversalReceiverDelegate: '0xa245bbda', LSP6KeyManager: '0x23f34c62', - LSP7DigitalAsset: '0x05519512', - LSP8IdentifiableDigitalAsset: '0x1ae9ba1f', + LSP7DigitalAsset: '0xdaa746b7', + LSP8IdentifiableDigitalAsset: '0x30dc5278', LSP9Vault: '0x28af17e6', LSP11BasicSocialRecovery: '0x049a28f1', LSP14Ownable2Step: '0x94be5999', diff --git a/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol b/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol index 8cb969b93..0f5f5031b 100644 --- a/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol +++ b/contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol @@ -50,11 +50,13 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y { * @dev Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its {`authorizedAmountFor(...)`} to `0`. * @param operator The address revoked from operating * @param tokenOwner The token owner + * @param notified Bool indicating whether the operator has been notified or not * @param operatorNotificationData The data to notify the operator about via LSP1. */ event RevokedOperator( address indexed operator, address indexed tokenOwner, + bool notified, bytes operatorNotificationData ); @@ -101,6 +103,7 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y { /** * @dev Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See {authorizedAmountFor}. + * Notify the operator based on the LSP1-UniversalReceiver standard * * @param operator The address to authorize as an operator. * @param amount The allowance amount of tokens operator has access to. @@ -123,6 +126,7 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y { * on behalf of the token owner (the caller of the function `msg.sender`). See also {authorizedAmountFor}. * * @param operator The address to revoke as an operator. + * @param notify Boolean indicating whether to notify the operator or not * @param operatorNotificationData The data to notify the operator about via LSP1. * * @custom:requirements @@ -133,6 +137,62 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y { */ function revokeOperator( address operator, + bool notify, + bytes memory operatorNotificationData + ) external; + + /** + * @custom:info This function in the LSP7 contract can be used as a prevention mechanism + * against double spending allowance vulnerability. + * + * @notice Increase the allowance of `operator` by +`addedAmount` + * + * @dev Atomically increases the allowance granted to `operator` by the caller. + * This is an alternative approach to {authorizeOperator} that can be used as a mitigation + * for the double spending allowance problem. + * Notify the operator based on the LSP1-UniversalReceiver standard + * + * @param operator The operator to increase the allowance for `msg.sender` + * @param addedAmount The additional amount to add on top of the current operator's allowance + * + * @custom:requirements + * - `operator` cannot be the same address as `msg.sender` + * - `operator` cannot be the zero address. + * + * @custom:events {AuthorizedOperator} indicating the updated allowance + */ + function increaseAllowance( + address operator, + uint256 addedAmount, + bytes memory operatorNotificationData + ) external; + + /** + * @custom:info This function in the LSP7 contract can be used as a prevention mechanism + * against the double spending allowance vulnerability. + * + * @notice Decrease the allowance of `operator` by -`subtractedAmount` + * + * @dev Atomically decreases the allowance granted to `operator` by the caller. + * This is an alternative approach to {authorizeOperator} that can be used as a mitigation + * for the double spending allowance problem. + * Notify the operator based on the LSP1-UniversalReceiver standard + * + * @custom:events + * - {AuthorizedOperator} event indicating the updated allowance after decreasing it. + * - {RevokeOperator} event if `subtractedAmount` is the full allowance, + * indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. + * + * @param operator The operator to decrease allowance for `msg.sender` + * @param subtractedAmount The amount to decrease by in the operator's allowance. + * + * @custom:requirements + * - `operator` cannot be the zero address. + * - `operator` must have allowance for the caller of at least `subtractedAmount`. + */ + function decreaseAllowance( + address operator, + uint256 subtractedAmount, bytes memory operatorNotificationData ) external; diff --git a/contracts/LSP7DigitalAsset/LSP7Constants.sol b/contracts/LSP7DigitalAsset/LSP7Constants.sol index bfbba9e1b..6878ea2ad 100644 --- a/contracts/LSP7DigitalAsset/LSP7Constants.sol +++ b/contracts/LSP7DigitalAsset/LSP7Constants.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; // --- ERC165 interface ids -bytes4 constant _INTERFACEID_LSP7 = 0x05519512; +bytes4 constant _INTERFACEID_LSP7 = 0xdaa746b7; // --- Token Hooks diff --git a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol index 3f01760cf..0470deb35 100644 --- a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol +++ b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol @@ -27,7 +27,8 @@ import { LSP7TokenOwnerCannotBeOperator, LSP7CannotSendWithAddressZero, LSP7NotifyTokenReceiverContractMissingLSP1Interface, - LSP7NotifyTokenReceiverIsEOA + LSP7NotifyTokenReceiverIsEOA, + OperatorAllowanceCannotBeIncreasedFromZero } from "./LSP7Errors.sol"; // constants @@ -113,13 +114,20 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { uint256 amount, bytes memory operatorNotificationData ) public virtual override { - _updateOperator(msg.sender, operator, amount, operatorNotificationData); + _updateOperator( + msg.sender, + operator, + amount, + true, + operatorNotificationData + ); bytes memory lsp1Data = abi.encode( msg.sender, amount, operatorNotificationData ); + _notifyTokenOperator(operator, lsp1Data); } @@ -128,16 +136,25 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { */ function revokeOperator( address operator, + bool notify, bytes memory operatorNotificationData ) public virtual override { - _updateOperator(msg.sender, operator, 0, operatorNotificationData); - - bytes memory lsp1Data = abi.encode( + _updateOperator( msg.sender, + operator, 0, + notify, operatorNotificationData ); - _notifyTokenOperator(operator, lsp1Data); + + if (notify) { + bytes memory lsp1Data = abi.encode( + msg.sender, + 0, + operatorNotificationData + ); + _notifyTokenOperator(operator, lsp1Data); + } } /** @@ -163,6 +180,69 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { return _operators[tokenOwner].values(); } + /** + * @inheritdoc ILSP7DigitalAsset + */ + function increaseAllowance( + address operator, + uint256 addedAmount, + bytes memory operatorNotificationData + ) public virtual override { + uint256 oldAllowance = authorizedAmountFor(operator, msg.sender); + if (oldAllowance == 0) + revert OperatorAllowanceCannotBeIncreasedFromZero(operator); + + uint256 newAllowance = oldAllowance + addedAmount; + + _updateOperator( + msg.sender, + operator, + newAllowance, + true, + operatorNotificationData + ); + + bytes memory lsp1Data = abi.encode( + msg.sender, + newAllowance, + operatorNotificationData + ); + _notifyTokenOperator(operator, lsp1Data); + } + + /** + * @inheritdoc ILSP7DigitalAsset + */ + function decreaseAllowance( + address operator, + uint256 subtractedAmount, + bytes memory operatorNotificationData + ) public virtual override { + uint256 currentAllowance = authorizedAmountFor(operator, msg.sender); + if (currentAllowance < subtractedAmount) { + revert LSP7DecreasedAllowanceBelowZero(); + } + + uint256 newAllowance; + unchecked { + newAllowance = currentAllowance - subtractedAmount; + _updateOperator( + msg.sender, + operator, + newAllowance, + true, + operatorNotificationData + ); + } + + bytes memory lsp1Data = abi.encode( + msg.sender, + newAllowance, + operatorNotificationData + ); + _notifyTokenOperator(operator, lsp1Data); + } + // --- Transfer functionality /** @@ -218,101 +298,6 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { } } - /** - * @custom: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. - * - * @notice Increase the allowance of `operator` by +`addedAmount` - * - * @dev Atomically increases the allowance granted to `operator` by the caller. - * This is an alternative approach to {authorizeOperator} that can be used as a mitigation - * for the double spending allowance problem. - * - * @param operator The operator to increase the allowance for `msg.sender` - * @param addedAmount The additional amount to add on top of the current operator's allowance - * - * @custom:requirements - * - `operator` cannot be the same address as `msg.sender` - * - `operator` cannot be the zero address. - * - * @custom:events {AuthorizedOperator} indicating the updated allowance - */ - function increaseAllowance( - address operator, - uint256 addedAmount, - bytes memory operatorNotificationData - ) public virtual { - uint256 newAllowance = authorizedAmountFor(operator, msg.sender) + - addedAmount; - - _updateOperator( - msg.sender, - operator, - newAllowance, - operatorNotificationData - ); - - bytes memory lsp1Data = abi.encode( - msg.sender, - newAllowance, - operatorNotificationData - ); - _notifyTokenOperator(operator, lsp1Data); - } - - /** - * @custom: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. - * - * @notice Decrease the allowance of `operator` by -`subtractedAmount` - * - * @dev Atomically decreases the allowance granted to `operator` by the caller. - * This is an alternative approach to {authorizeOperator} that can be used as a mitigation - * for the double spending allowance problem. - * - * @custom:events - * - {AuthorizedOperator} event indicating the updated allowance after decreasing it. - * - {RevokeOperator} event if `subtractedAmount` is the full allowance, - * indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. - * - * @param operator The operator to decrease allowance for `msg.sender` - * @param subtractedAmount The amount to decrease by in the operator's allowance. - * - * @custom:requirements - * - `operator` cannot be the zero address. - * - `operator` must have allowance for the caller of at least `subtractedAmount`. - */ - function decreaseAllowance( - address operator, - uint256 subtractedAmount, - bytes memory operatorNotificationData - ) public virtual { - uint256 currentAllowance = authorizedAmountFor(operator, msg.sender); - if (currentAllowance < subtractedAmount) { - revert LSP7DecreasedAllowanceBelowZero(); - } - - uint256 newAllowance; - unchecked { - newAllowance = currentAllowance - subtractedAmount; - _updateOperator( - msg.sender, - operator, - newAllowance, - operatorNotificationData - ); - } - - bytes memory lsp1Data = abi.encode( - msg.sender, - newAllowance, - operatorNotificationData - ); - _notifyTokenOperator(operator, lsp1Data); - } - /** * @dev Changes token `amount` the `operator` has access to from `tokenOwner` tokens. * If the amount is zero the operator is removed from the list of operators, otherwise he is added to the list of operators. @@ -321,6 +306,8 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { * @param tokenOwner The address that will give `operator` an allowance for on its balance. * @param operator The address to grant an allowance to spend. * @param allowance The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. + * @param notified Boolean indicating whether the operator has been notified about the change of allowance + * @param operatorNotificationData The data to send to the universalReceiver function of the operator in case of notifying * * @custom:events * - {RevokedOperator} event when operator's allowance is set to `0`. @@ -334,6 +321,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { address tokenOwner, address operator, uint256 allowance, + bool notified, bytes memory operatorNotificationData ) internal virtual { if (operator == address(0)) { @@ -359,6 +347,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { emit RevokedOperator( operator, tokenOwner, + notified, operatorNotificationData ); } @@ -506,6 +495,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { tokenOwner: tokenOwner, operator: operator, allowance: authorizedAmount - amountToSpend, + notified: false, operatorNotificationData: "" }); } @@ -616,16 +606,10 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { _INTERFACEID_LSP1 ) ) { - try - ILSP1UniversalReceiver(operator).universalReceiver( - _TYPEID_LSP7_TOKENOPERATOR, - lsp1Data - ) - { - return; - } catch { - return; - } + ILSP1UniversalReceiver(operator).universalReceiver( + _TYPEID_LSP7_TOKENOPERATOR, + lsp1Data + ); } } diff --git a/contracts/LSP7DigitalAsset/LSP7Errors.sol b/contracts/LSP7DigitalAsset/LSP7Errors.sol index cc9388941..315db1c5b 100644 --- a/contracts/LSP7DigitalAsset/LSP7Errors.sol +++ b/contracts/LSP7DigitalAsset/LSP7Errors.sol @@ -77,3 +77,8 @@ error LSP7DecreasedAllowanceBelowZero(); * @notice LSP7 contract cannot receive native tokens. */ error LSP7TokenContractCannotHoldValue(); + +/** + * @dev Reverts when token owner call {increaseAllowance} for an operator that does not have any allowance + */ +error OperatorAllowanceCannotBeIncreasedFromZero(address operator); diff --git a/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol b/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol index bc7cfa546..b8326007b 100644 --- a/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol +++ b/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol @@ -140,7 +140,12 @@ abstract contract LSP7CompatibleERC20 is IERC20Metadata, LSP7DigitalAsset { address operator, uint256 amount ) public virtual returns (bool) { - authorizeOperator(operator, amount, ""); + if (amount != 0) { + authorizeOperator(operator, amount, ""); + } else { + revokeOperator(operator, false, ""); + } + return true; } @@ -193,12 +198,14 @@ abstract contract LSP7CompatibleERC20 is IERC20Metadata, LSP7DigitalAsset { address tokenOwner, address operator, uint256 amount, + bool notified, bytes memory operatorNotificationData ) internal virtual override { super._updateOperator( tokenOwner, operator, amount, + notified, operatorNotificationData ); emit IERC20.Approval(tokenOwner, operator, amount); diff --git a/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20InitAbstract.sol b/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20InitAbstract.sol index 840e537ed..8f0d9dec0 100644 --- a/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20InitAbstract.sol +++ b/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20InitAbstract.sol @@ -153,7 +153,11 @@ abstract contract LSP7CompatibleERC20InitAbstract is address operator, uint256 amount ) public virtual returns (bool) { - authorizeOperator(operator, amount, ""); + if (amount != 0) { + authorizeOperator(operator, amount, ""); + } else { + revokeOperator(operator, false, ""); + } return true; } @@ -206,12 +210,14 @@ abstract contract LSP7CompatibleERC20InitAbstract is address tokenOwner, address operator, uint256 amount, + bool notified, bytes memory operatorNotificationData ) internal virtual override { super._updateOperator( tokenOwner, operator, amount, + notified, operatorNotificationData ); emit IERC20.Approval(tokenOwner, operator, amount); diff --git a/contracts/LSP8IdentifiableDigitalAsset/ILSP8IdentifiableDigitalAsset.sol b/contracts/LSP8IdentifiableDigitalAsset/ILSP8IdentifiableDigitalAsset.sol index 53aec6be2..918289303 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/ILSP8IdentifiableDigitalAsset.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/ILSP8IdentifiableDigitalAsset.sol @@ -51,12 +51,14 @@ interface ILSP8IdentifiableDigitalAsset is IERC165, IERC725Y { * @param operator The address revoked from the operator array ({getOperatorsOf}). * @param tokenOwner The owner of the `tokenId`. * @param tokenId The tokenId `operator` is revoked from operating on. + * @param notified Bool indicating whether the operator has been notified or not * @param operatorNotificationData The data to notify the operator about via LSP1. */ event RevokedOperator( address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, + bool notified, bytes operatorNotificationData ); @@ -102,6 +104,7 @@ interface ILSP8IdentifiableDigitalAsset is IERC165, IERC725Y { /** * @dev Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See {isOperatorFor}. + * Notify the operator based on the LSP1-UniversalReceiver standard * * @param operator The address to authorize as an operator. * @param tokenId The token ID operator has access to. @@ -127,6 +130,7 @@ interface ILSP8IdentifiableDigitalAsset is IERC165, IERC725Y { * * @param operator The address to revoke as an operator. * @param tokenId The tokenId `operator` is revoked from operating on. + * @param notify Boolean indicating whether to notify the operator or not * @param operatorNotificationData The data to notify the operator about via LSP1. * * @custom:requirements @@ -140,6 +144,7 @@ interface ILSP8IdentifiableDigitalAsset is IERC165, IERC725Y { function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes memory operatorNotificationData ) external; diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol index a117eeaf1..f059c13af 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; // --- ERC165 interface ids -bytes4 constant _INTERFACEID_LSP8 = 0x1ae9ba1f; +bytes4 constant _INTERFACEID_LSP8 = 0x30dc5278; // --- ERC725Y Data Keys diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol index 3a0d0e995..e8c13a499 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol @@ -149,8 +149,10 @@ abstract contract LSP8IdentifiableDigitalAssetCore is bytes memory lsp1Data = abi.encode( msg.sender, tokenId, + true, // authorized operatorNotificationData ); + _notifyTokenOperator(operator, lsp1Data); } @@ -160,6 +162,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes memory operatorNotificationData ) public virtual override { address tokenOwner = tokenOwnerOf(tokenId); @@ -180,15 +183,19 @@ abstract contract LSP8IdentifiableDigitalAssetCore is operator, tokenOwner, tokenId, + notify, operatorNotificationData ); - bytes memory lsp1Data = abi.encode( - msg.sender, - tokenId, - operatorNotificationData - ); - _notifyTokenOperator(operator, lsp1Data); + if (notify) { + bytes memory lsp1Data = abi.encode( + msg.sender, + tokenId, + false, // unauthorized + operatorNotificationData + ); + _notifyTokenOperator(operator, lsp1Data); + } } /** @@ -281,6 +288,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes memory operatorNotificationData ) internal virtual { bool isRemoved = _operators[tokenId].remove(operator); @@ -290,6 +298,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is operator, tokenOwner, tokenId, + notified, operatorNotificationData ); } @@ -319,7 +328,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is for (uint256 i; i < operatorListLength; ) { // we are emptying the list, always remove from index 0 operator = operatorsForTokenId.at(0); - _revokeOperator(operator, tokenOwner, tokenId, ""); + _revokeOperator(operator, tokenOwner, tokenId, false, ""); unchecked { ++i; @@ -563,16 +572,10 @@ abstract contract LSP8IdentifiableDigitalAssetCore is _INTERFACEID_LSP1 ) ) { - try - ILSP1UniversalReceiver(operator).universalReceiver( - _TYPEID_LSP8_TOKENOPERATOR, - lsp1Data - ) - { - return; - } catch { - return; - } + ILSP1UniversalReceiver(operator).universalReceiver( + _TYPEID_LSP8_TOKENOPERATOR, + lsp1Data + ); } } diff --git a/contracts/Mocks/Tokens/TokenReceiverWithLSP1Revert.sol b/contracts/Mocks/Tokens/TokenReceiverWithLSP1Revert.sol new file mode 100644 index 000000000..d0e35a7bb --- /dev/null +++ b/contracts/Mocks/Tokens/TokenReceiverWithLSP1Revert.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.4; + +// interfaces +import { + ILSP1UniversalReceiver +} from "../../LSP1UniversalReceiver/ILSP1UniversalReceiver.sol"; + +// modules +import { + ERC165Storage +} from "@openzeppelin/contracts/utils/introspection/ERC165Storage.sol"; + +// constants +import {_INTERFACEID_LSP1} from "../../LSP1UniversalReceiver/LSP1Constants.sol"; + +contract TokenReceiverWithLSP1Revert is ERC165Storage, ILSP1UniversalReceiver { + function addLSP1Support() public { + _registerInterface(_INTERFACEID_LSP1); + } + + function universalReceiver( + bytes32 /*typeId*/, + bytes memory /*data*/ + ) external payable override returns (bytes memory) { + revert("I reverted"); + } +} diff --git a/docs/_interface_ids_table.mdx b/docs/_interface_ids_table.mdx index fbfb9856d..f7dd19f4a 100644 --- a/docs/_interface_ids_table.mdx +++ b/docs/_interface_ids_table.mdx @@ -8,8 +8,8 @@ | **LSP1UniversalReceiver** | `0x6bb56a14` | Interface of the LSP1 - Universal Receiver standard, an entry function for a contract to receive arbitrary information. | | **LSP1UniversalReceiverDelegate** | `0xa245bbda` | Interface of the LSP1 - Universal Receiver Delegate standard. | | **LSP6KeyManager** | `0x23f34c62` | Interface of the LSP6 - Key Manager standard, a contract acting as a controller of an ERC725 Account using predfined permissions. | -| **LSP7DigitalAsset** | `0x05519512` | Interface of the LSP7 - Digital Asset standard, a fungible digital asset. | -| **LSP8IdentifiableDigitalAsset** | `0x1ae9ba1f` | Interface of the LSP8 - Identifiable Digital Asset standard, a non-fungible digital asset. | +| **LSP7DigitalAsset** | `0xdaa746b7` | Interface of the LSP7 - Digital Asset standard, a fungible digital asset. | +| **LSP8IdentifiableDigitalAsset** | `0x30dc5278` | Interface of the LSP8 - Identifiable Digital Asset standard, a non-fungible digital asset. | | **LSP9Vault** | `0x28af17e6` | Interface of LSP9 - Vault standard, a blockchain vault that can hold assets and interact with other smart contracts. | | **LSP11BasicSocialRecovery** | `0x049a28f1` | Interface of the LSP11 - Basic Social Recovery standard, a contract to recover access control into an account. | | **LSP14Ownable2Step** | `0x94be5999` | Interface of the LSP14 - Ownable 2-step standard, an extension of the [EIP173] (Ownable) standard with 2-step process to transfer or renounce ownership. | diff --git a/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md index 047f5ab1a..a9a3d5501 100644 --- a/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md +++ b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md @@ -102,7 +102,7 @@ function authorizeOperator( ) external nonpayable; ``` -Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). +Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -216,12 +216,6 @@ Returns the number of decimals used to get its user representation. If the asset ::: -:::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, @@ -232,25 +226,7 @@ function decreaseAllowance( _Decrease the allowance of `operator` by -`subtractedAmount`_ -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 `subtractedAmount`. - -
- -
- -**Emitted events:** - -- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. -- [`RevokeOperator`](#revokeoperator) event if `subtractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -372,12 +348,6 @@ Returns all `operator` addresses that are allowed to transfer or burn on behalf ::: -:::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, @@ -388,24 +358,7 @@ function increaseAllowance( _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 - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -467,14 +420,15 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol) -- Function signature: `revokeOperator(address,bytes)` -- Function selector: `0xca3631e7` +- Function signature: `revokeOperator(address,bool,bytes)` +- Function selector: `0x4521748e` ::: ```solidity function revokeOperator( address operator, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -483,10 +437,11 @@ Removes the `operator` address as an operator of callers tokens, disallowing it #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -812,6 +767,7 @@ function _updateOperator( address tokenOwner, address operator, uint256 allowance, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -822,12 +778,13 @@ If the amount is zero then the operator is being revoked, otherwise the operator #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | -------------------------------------------------------------------------------------- | -| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | -| `operator` | `address` | The address to grant an allowance to spend. | -| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | -| `operatorNotificationData` | `bytes` | - | +| Name | Type | Description | +| -------------------------- | :-------: | ----------------------------------------------------------------------------------------------------------------------- | +| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | +| `operator` | `address` | @param operatorNotificationData The data to send to the universalReceiver function of the operator in case of notifying | +| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | +| `notified` | `bool` | Boolean indicating whether the operator has been notified about the change of allowance | +| `operatorNotificationData` | `bytes` | The data to send to the universalReceiver function of the operator in case of notifying |
@@ -1250,24 +1207,25 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol) -- Event signature: `RevokedOperator(address,address,bytes)` -- Event topic hash: `0x9ebfc34ce0da1178c4be66252d63a8a173d733c4bbb049241ce142dc4f0e0228` +- Event signature: `RevokedOperator(address,address,bool,bytes)` +- Event topic hash: `0x66015c8835ee443e5bc280176609215a5035da4bae05bdef994596d7e43aae22` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its [`authorizedAmountFor(...)`](#`authorizedamountfor) to `0`. #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from operating | -| `tokenOwner` **`indexed`** | `address` | The token owner | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from operating | +| `tokenOwner` **`indexed`** | `address` | The token owner | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1730,6 +1688,31 @@ reverts when there is no extension for the function selector being called with
+### OperatorAllowanceCannotBeIncreasedFromZero + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#operatorallowancecannotbeincreasedfromzero) +- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol) +- Error signature: `OperatorAllowanceCannotBeIncreasedFromZero(address)` +- Error hash: `0xcba6e977` + +::: + +```solidity +error OperatorAllowanceCannotBeIncreasedFromZero(address operator); +``` + +Reverts when token owner call [`increaseAllowance`](#increaseallowance) for an operator that does not have any allowance + +#### Parameters + +| Name | Type | Description | +| ---------- | :-------: | ----------- | +| `operator` | `address` | - | + +
+ ### OwnableCallerNotTheOwner :::note References diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md index f0c7d2b96..f3d48647a 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md @@ -100,7 +100,7 @@ function authorizeOperator( ) external nonpayable; ``` -Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). +Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -241,12 +241,6 @@ Returns the number of decimals used to get its user representation. If the asset ::: -:::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, @@ -257,25 +251,7 @@ function decreaseAllowance( _Decrease the allowance of `operator` by -`subtractedAmount`_ -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 `subtractedAmount`. - -
- -
- -**Emitted events:** - -- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. -- [`RevokeOperator`](#revokeoperator) event if `subtractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -397,12 +373,6 @@ Returns all `operator` addresses that are allowed to transfer or burn on behalf ::: -:::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, @@ -413,24 +383,7 @@ function increaseAllowance( _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 - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -492,14 +445,15 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP7Burnable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.sol) -- Function signature: `revokeOperator(address,bytes)` -- Function selector: `0xca3631e7` +- Function signature: `revokeOperator(address,bool,bytes)` +- Function selector: `0x4521748e` ::: ```solidity function revokeOperator( address operator, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -508,10 +462,11 @@ Removes the `operator` address as an operator of callers tokens, disallowing it #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -837,6 +792,7 @@ function _updateOperator( address tokenOwner, address operator, uint256 allowance, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -847,12 +803,13 @@ If the amount is zero then the operator is being revoked, otherwise the operator #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | -------------------------------------------------------------------------------------- | -| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | -| `operator` | `address` | The address to grant an allowance to spend. | -| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | -| `operatorNotificationData` | `bytes` | - | +| Name | Type | Description | +| -------------------------- | :-------: | ----------------------------------------------------------------------------------------------------------------------- | +| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | +| `operator` | `address` | @param operatorNotificationData The data to send to the universalReceiver function of the operator in case of notifying | +| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | +| `notified` | `bool` | Boolean indicating whether the operator has been notified about the change of allowance | +| `operatorNotificationData` | `bytes` | The data to send to the universalReceiver function of the operator in case of notifying |
@@ -1275,24 +1232,25 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP7Burnable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.sol) -- Event signature: `RevokedOperator(address,address,bytes)` -- Event topic hash: `0x9ebfc34ce0da1178c4be66252d63a8a173d733c4bbb049241ce142dc4f0e0228` +- Event signature: `RevokedOperator(address,address,bool,bytes)` +- Event topic hash: `0x66015c8835ee443e5bc280176609215a5035da4bae05bdef994596d7e43aae22` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its [`authorizedAmountFor(...)`](#`authorizedamountfor) to `0`. #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from operating | -| `tokenOwner` **`indexed`** | `address` | The token owner | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from operating | +| `tokenOwner` **`indexed`** | `address` | The token owner | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1755,6 +1713,31 @@ reverts when there is no extension for the function selector being called with
+### OperatorAllowanceCannotBeIncreasedFromZero + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#operatorallowancecannotbeincreasedfromzero) +- Solidity implementation: [`LSP7Burnable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.sol) +- Error signature: `OperatorAllowanceCannotBeIncreasedFromZero(address)` +- Error hash: `0xcba6e977` + +::: + +```solidity +error OperatorAllowanceCannotBeIncreasedFromZero(address operator); +``` + +Reverts when token owner call [`increaseAllowance`](#increaseallowance) for an operator that does not have any allowance + +#### Parameters + +| Name | Type | Description | +| ---------- | :-------: | ----------- | +| `operator` | `address` | - | + +
+ ### OwnableCallerNotTheOwner :::note References diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md index 028cf0a49..173ef256f 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md @@ -100,7 +100,7 @@ function authorizeOperator( ) external nonpayable; ``` -Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). +Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -214,12 +214,6 @@ Returns the number of decimals used to get its user representation. If the asset ::: -:::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, @@ -230,25 +224,7 @@ function decreaseAllowance( _Decrease the allowance of `operator` by -`subtractedAmount`_ -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 `subtractedAmount`. - -
- -
- -**Emitted events:** - -- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. -- [`RevokeOperator`](#revokeoperator) event if `subtractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -370,12 +346,6 @@ Returns all `operator` addresses that are allowed to transfer or burn on behalf ::: -:::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, @@ -386,24 +356,7 @@ function increaseAllowance( _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 - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -465,14 +418,15 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP7CappedSupply.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.sol) -- Function signature: `revokeOperator(address,bytes)` -- Function selector: `0xca3631e7` +- Function signature: `revokeOperator(address,bool,bytes)` +- Function selector: `0x4521748e` ::: ```solidity function revokeOperator( address operator, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -481,10 +435,11 @@ Removes the `operator` address as an operator of callers tokens, disallowing it #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -837,6 +792,7 @@ function _updateOperator( address tokenOwner, address operator, uint256 allowance, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -847,12 +803,13 @@ If the amount is zero then the operator is being revoked, otherwise the operator #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | -------------------------------------------------------------------------------------- | -| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | -| `operator` | `address` | The address to grant an allowance to spend. | -| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | -| `operatorNotificationData` | `bytes` | - | +| Name | Type | Description | +| -------------------------- | :-------: | ----------------------------------------------------------------------------------------------------------------------- | +| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | +| `operator` | `address` | @param operatorNotificationData The data to send to the universalReceiver function of the operator in case of notifying | +| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | +| `notified` | `bool` | Boolean indicating whether the operator has been notified about the change of allowance | +| `operatorNotificationData` | `bytes` | The data to send to the universalReceiver function of the operator in case of notifying |
@@ -1249,24 +1206,25 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP7CappedSupply.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.sol) -- Event signature: `RevokedOperator(address,address,bytes)` -- Event topic hash: `0x9ebfc34ce0da1178c4be66252d63a8a173d733c4bbb049241ce142dc4f0e0228` +- Event signature: `RevokedOperator(address,address,bool,bytes)` +- Event topic hash: `0x66015c8835ee443e5bc280176609215a5035da4bae05bdef994596d7e43aae22` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its [`authorizedAmountFor(...)`](#`authorizedamountfor) to `0`. #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from operating | -| `tokenOwner` **`indexed`** | `address` | The token owner | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from operating | +| `tokenOwner` **`indexed`** | `address` | The token owner | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1771,6 +1729,31 @@ reverts when there is no extension for the function selector being called with
+### OperatorAllowanceCannotBeIncreasedFromZero + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#operatorallowancecannotbeincreasedfromzero) +- Solidity implementation: [`LSP7CappedSupply.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.sol) +- Error signature: `OperatorAllowanceCannotBeIncreasedFromZero(address)` +- Error hash: `0xcba6e977` + +::: + +```solidity +error OperatorAllowanceCannotBeIncreasedFromZero(address operator); +``` + +Reverts when token owner call [`increaseAllowance`](#increaseallowance) for an operator that does not have any allowance + +#### Parameters + +| Name | Type | Description | +| ---------- | :-------: | ----------- | +| `operator` | `address` | - | + +
+ ### OwnableCallerNotTheOwner :::note References diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md index 3792dc399..6446fdb8a 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md @@ -170,7 +170,7 @@ function authorizeOperator( ) external nonpayable; ``` -Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). +Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -284,12 +284,6 @@ Returns the number of decimals used to get its user representation. If the asset ::: -:::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, @@ -300,25 +294,7 @@ function decreaseAllowance( _Decrease the allowance of `operator` by -`subtractedAmount`_ -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 `subtractedAmount`. - -
- -
- -**Emitted events:** - -- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. -- [`RevokeOperator`](#revokeoperator) event if `subtractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -440,12 +416,6 @@ Returns all `operator` addresses that are allowed to transfer or burn on behalf ::: -:::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, @@ -456,24 +426,7 @@ function increaseAllowance( _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 - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -560,14 +513,15 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP7CompatibleERC20.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol) -- Function signature: `revokeOperator(address,bytes)` -- Function selector: `0xca3631e7` +- Function signature: `revokeOperator(address,bool,bytes)` +- Function selector: `0x4521748e` ::: ```solidity function revokeOperator( address operator, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -576,10 +530,11 @@ Removes the `operator` address as an operator of callers tokens, disallowing it #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1014,6 +969,7 @@ function _updateOperator( address tokenOwner, address operator, uint256 amount, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1366,24 +1322,25 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP7CompatibleERC20.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol) -- Event signature: `RevokedOperator(address,address,bytes)` -- Event topic hash: `0x9ebfc34ce0da1178c4be66252d63a8a173d733c4bbb049241ce142dc4f0e0228` +- Event signature: `RevokedOperator(address,address,bool,bytes)` +- Event topic hash: `0x66015c8835ee443e5bc280176609215a5035da4bae05bdef994596d7e43aae22` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its [`authorizedAmountFor(...)`](#`authorizedamountfor) to `0`. #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from operating | -| `tokenOwner` **`indexed`** | `address` | The token owner | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from operating | +| `tokenOwner` **`indexed`** | `address` | The token owner | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1873,6 +1830,31 @@ reverts when there is no extension for the function selector being called with
+### OperatorAllowanceCannotBeIncreasedFromZero + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#operatorallowancecannotbeincreasedfromzero) +- Solidity implementation: [`LSP7CompatibleERC20.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol) +- Error signature: `OperatorAllowanceCannotBeIncreasedFromZero(address)` +- Error hash: `0xcba6e977` + +::: + +```solidity +error OperatorAllowanceCannotBeIncreasedFromZero(address operator); +``` + +Reverts when token owner call [`increaseAllowance`](#increaseallowance) for an operator that does not have any allowance + +#### Parameters + +| Name | Type | Description | +| ---------- | :-------: | ----------- | +| `operator` | `address` | - | + +
+ ### OwnableCallerNotTheOwner :::note References diff --git a/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md b/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md index 63ac7291f..d07dd2ce0 100644 --- a/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md +++ b/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md @@ -171,7 +171,7 @@ function authorizeOperator( ) external nonpayable; ``` -Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). +Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -285,12 +285,6 @@ Returns the number of decimals used to get its user representation. If the asset ::: -:::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, @@ -301,25 +295,7 @@ function decreaseAllowance( _Decrease the allowance of `operator` by -`subtractedAmount`_ -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 `subtractedAmount`. - -
- -
- -**Emitted events:** - -- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. -- [`RevokeOperator`](#revokeoperator) event if `subtractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -441,12 +417,6 @@ Returns all `operator` addresses that are allowed to transfer or burn on behalf ::: -:::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, @@ -457,24 +427,7 @@ function increaseAllowance( _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 - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -594,14 +547,15 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP7CompatibleERC20Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.sol) -- Function signature: `revokeOperator(address,bytes)` -- Function selector: `0xca3631e7` +- Function signature: `revokeOperator(address,bool,bytes)` +- Function selector: `0x4521748e` ::: ```solidity function revokeOperator( address operator, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -610,10 +564,11 @@ Removes the `operator` address as an operator of callers tokens, disallowing it #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1048,6 +1003,7 @@ function _updateOperator( address tokenOwner, address operator, uint256 amount, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1400,24 +1356,25 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP7CompatibleERC20Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.sol) -- Event signature: `RevokedOperator(address,address,bytes)` -- Event topic hash: `0x9ebfc34ce0da1178c4be66252d63a8a173d733c4bbb049241ce142dc4f0e0228` +- Event signature: `RevokedOperator(address,address,bool,bytes)` +- Event topic hash: `0x66015c8835ee443e5bc280176609215a5035da4bae05bdef994596d7e43aae22` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its [`authorizedAmountFor(...)`](#`authorizedamountfor) to `0`. #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from operating | -| `tokenOwner` **`indexed`** | `address` | The token owner | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from operating | +| `tokenOwner` **`indexed`** | `address` | The token owner | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1907,6 +1864,31 @@ reverts when there is no extension for the function selector being called with
+### OperatorAllowanceCannotBeIncreasedFromZero + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#operatorallowancecannotbeincreasedfromzero) +- Solidity implementation: [`LSP7CompatibleERC20Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.sol) +- Error signature: `OperatorAllowanceCannotBeIncreasedFromZero(address)` +- Error hash: `0xcba6e977` + +::: + +```solidity +error OperatorAllowanceCannotBeIncreasedFromZero(address operator); +``` + +Reverts when token owner call [`increaseAllowance`](#increaseallowance) for an operator that does not have any allowance + +#### Parameters + +| Name | Type | Description | +| ---------- | :-------: | ----------- | +| `operator` | `address` | - | + +
+ ### OwnableCallerNotTheOwner :::note References diff --git a/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md b/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md index a178eff9e..15bbe3d03 100644 --- a/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md +++ b/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md @@ -131,7 +131,7 @@ function authorizeOperator( ) external nonpayable; ``` -Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). +Sets an `amount` of tokens that an `operator` has access from the caller's balance (allowance). See [`authorizedAmountFor`](#authorizedamountfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -245,12 +245,6 @@ Returns the number of decimals used to get its user representation. If the asset ::: -:::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, @@ -261,25 +255,7 @@ function decreaseAllowance( _Decrease the allowance of `operator` by -`subtractedAmount`_ -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 `subtractedAmount`. - -
- -
- -**Emitted events:** - -- [`AuthorizedOperator`](#authorizedoperator) event indicating the updated allowance after decreasing it. -- [`RevokeOperator`](#revokeoperator) event if `subtractedAmount` is the full allowance, indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`. - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -401,12 +377,6 @@ Returns all `operator` addresses that are allowed to transfer or burn on behalf ::: -:::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, @@ -417,24 +387,7 @@ function increaseAllowance( _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 - -
+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. Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -529,14 +482,15 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP7Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7Mintable.sol) -- Function signature: `revokeOperator(address,bytes)` -- Function selector: `0xca3631e7` +- Function signature: `revokeOperator(address,bool,bytes)` +- Function selector: `0x4521748e` ::: ```solidity function revokeOperator( address operator, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -545,10 +499,11 @@ Removes the `operator` address as an operator of callers tokens, disallowing it #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -874,6 +829,7 @@ function _updateOperator( address tokenOwner, address operator, uint256 allowance, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -884,12 +840,13 @@ If the amount is zero then the operator is being revoked, otherwise the operator #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | -------------------------------------------------------------------------------------- | -| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | -| `operator` | `address` | The address to grant an allowance to spend. | -| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | -| `operatorNotificationData` | `bytes` | - | +| Name | Type | Description | +| -------------------------- | :-------: | ----------------------------------------------------------------------------------------------------------------------- | +| `tokenOwner` | `address` | The address that will give `operator` an allowance for on its balance. | +| `operator` | `address` | @param operatorNotificationData The data to send to the universalReceiver function of the operator in case of notifying | +| `allowance` | `uint256` | The maximum amount of token that `operator` can spend from the `tokenOwner`'s balance. | +| `notified` | `bool` | Boolean indicating whether the operator has been notified about the change of allowance | +| `operatorNotificationData` | `bytes` | The data to send to the universalReceiver function of the operator in case of notifying |
@@ -1312,24 +1269,25 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP7Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7Mintable.sol) -- Event signature: `RevokedOperator(address,address,bytes)` -- Event topic hash: `0x9ebfc34ce0da1178c4be66252d63a8a173d733c4bbb049241ce142dc4f0e0228` +- Event signature: `RevokedOperator(address,address,bool,bytes)` +- Event topic hash: `0x66015c8835ee443e5bc280176609215a5035da4bae05bdef994596d7e43aae22` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` for `amount` tokens and set its [`authorizedAmountFor(...)`](#`authorizedamountfor) to `0`. #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ----------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from operating | -| `tokenOwner` **`indexed`** | `address` | The token owner | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from operating | +| `tokenOwner` **`indexed`** | `address` | The token owner | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1792,6 +1750,31 @@ reverts when there is no extension for the function selector being called with
+### OperatorAllowanceCannotBeIncreasedFromZero + +:::note References + +- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#operatorallowancecannotbeincreasedfromzero) +- Solidity implementation: [`LSP7Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7Mintable.sol) +- Error signature: `OperatorAllowanceCannotBeIncreasedFromZero(address)` +- Error hash: `0xcba6e977` + +::: + +```solidity +error OperatorAllowanceCannotBeIncreasedFromZero(address operator); +``` + +Reverts when token owner call [`increaseAllowance`](#increaseallowance) for an operator that does not have any allowance + +#### Parameters + +| Name | Type | Description | +| ---------- | :-------: | ----------- | +| `operator` | `address` | - | + +
+ ### OwnableCallerNotTheOwner :::note References diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md b/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md index 33a2ba6d7..0822f4511 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md @@ -96,7 +96,7 @@ function authorizeOperator( ) external nonpayable; ``` -Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -323,8 +323,8 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP8IdentifiableDigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol) -- Function signature: `revokeOperator(address,bytes32,bytes)` -- Function selector: `0xf1b97e04` +- Function signature: `revokeOperator(address,bytes32,bool,bytes)` +- Function selector: `0xdb8c9663` ::: @@ -332,6 +332,7 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -340,11 +341,12 @@ Remove access of `operator` for a given `tokenId`, disallowing it to transfer `t #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ---------------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -752,6 +754,7 @@ function _revokeOperator( address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1183,13 +1186,13 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP8IdentifiableDigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol) -- Event signature: `RevokedOperator(address,address,bytes32,bytes)` -- Event topic hash: `0x501bc920d7f604417e315bcf29247652b2327fa1076b27b7f132bd8927cb15ea` +- Event signature: `RevokedOperator(address,address,bytes32,bool,bytes)` +- Event topic hash: `0x3ee932cea40ebbbfd8577d47156cc17cce8683802c57bbd1fb8c131c6f07af0a` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. @@ -1201,6 +1204,7 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i | `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. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | | `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md index 181bbd5b7..23a43c7a7 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md @@ -94,7 +94,7 @@ function authorizeOperator( ) external nonpayable; ``` -Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -349,8 +349,8 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP8Burnable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.sol) -- Function signature: `revokeOperator(address,bytes32,bytes)` -- Function selector: `0xf1b97e04` +- Function signature: `revokeOperator(address,bytes32,bool,bytes)` +- Function selector: `0xdb8c9663` ::: @@ -358,6 +358,7 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -366,11 +367,12 @@ Remove access of `operator` for a given `tokenId`, disallowing it to transfer `t #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ---------------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -778,6 +780,7 @@ function _revokeOperator( address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1209,13 +1212,13 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP8Burnable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.sol) -- Event signature: `RevokedOperator(address,address,bytes32,bytes)` -- Event topic hash: `0x501bc920d7f604417e315bcf29247652b2327fa1076b27b7f132bd8927cb15ea` +- Event signature: `RevokedOperator(address,address,bytes32,bool,bytes)` +- Event topic hash: `0x3ee932cea40ebbbfd8577d47156cc17cce8683802c57bbd1fb8c131c6f07af0a` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. @@ -1227,6 +1230,7 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i | `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. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | | `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md index 53a5b5f12..675c4d575 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md @@ -94,7 +94,7 @@ function authorizeOperator( ) external nonpayable; ``` -Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -321,8 +321,8 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP8CappedSupply.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.sol) -- Function signature: `revokeOperator(address,bytes32,bytes)` -- Function selector: `0xf1b97e04` +- Function signature: `revokeOperator(address,bytes32,bool,bytes)` +- Function selector: `0xdb8c9663` ::: @@ -330,6 +330,7 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -338,11 +339,12 @@ Remove access of `operator` for a given `tokenId`, disallowing it to transfer `t #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ---------------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -777,6 +779,7 @@ function _revokeOperator( address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1183,13 +1186,13 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP8CappedSupply.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.sol) -- Event signature: `RevokedOperator(address,address,bytes32,bytes)` -- Event topic hash: `0x501bc920d7f604417e315bcf29247652b2327fa1076b27b7f132bd8927cb15ea` +- Event signature: `RevokedOperator(address,address,bytes32,bool,bytes)` +- Event topic hash: `0x3ee932cea40ebbbfd8577d47156cc17cce8683802c57bbd1fb8c131c6f07af0a` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. @@ -1201,6 +1204,7 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i | `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. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | | `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md index d82124210..929364fe0 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md @@ -122,7 +122,7 @@ function authorizeOperator( ) external nonpayable; ``` -Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). Notify the operator based on the LSP1-UniversalReceiver standard
@@ -486,8 +486,8 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP8CompatibleERC721.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol) -- Function signature: `revokeOperator(address,bytes32,bytes)` -- Function selector: `0xf1b97e04` +- Function signature: `revokeOperator(address,bytes32,bool,bytes)` +- Function selector: `0xdb8c9663` ::: @@ -495,6 +495,7 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -503,11 +504,12 @@ Remove access of `operator` for a given `tokenId`, disallowing it to transfer `t #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ---------------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1128,6 +1130,7 @@ function _revokeOperator( address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1547,13 +1550,13 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP8CompatibleERC721.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol) -- Event signature: `RevokedOperator(address,address,bytes32,bytes)` -- Event topic hash: `0x501bc920d7f604417e315bcf29247652b2327fa1076b27b7f132bd8927cb15ea` +- Event signature: `RevokedOperator(address,address,bytes32,bool,bytes)` +- Event topic hash: `0x3ee932cea40ebbbfd8577d47156cc17cce8683802c57bbd1fb8c131c6f07af0a` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. @@ -1565,6 +1568,7 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i | `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. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | | `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md index eea20e1d9..e1f0fd81c 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md @@ -94,7 +94,7 @@ function authorizeOperator( ) external nonpayable; ``` -Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -321,8 +321,8 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP8Enumerable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.sol) -- Function signature: `revokeOperator(address,bytes32,bytes)` -- Function selector: `0xf1b97e04` +- Function signature: `revokeOperator(address,bytes32,bool,bytes)` +- Function selector: `0xdb8c9663` ::: @@ -330,6 +330,7 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -338,11 +339,12 @@ Remove access of `operator` for a given `tokenId`, disallowing it to transfer `t #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ---------------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -783,6 +785,7 @@ function _revokeOperator( address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1211,13 +1214,13 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP8Enumerable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.sol) -- Event signature: `RevokedOperator(address,address,bytes32,bytes)` -- Event topic hash: `0x501bc920d7f604417e315bcf29247652b2327fa1076b27b7f132bd8927cb15ea` +- Event signature: `RevokedOperator(address,address,bytes32,bool,bytes)` +- Event topic hash: `0x3ee932cea40ebbbfd8577d47156cc17cce8683802c57bbd1fb8c131c6f07af0a` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. @@ -1229,6 +1232,7 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i | `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. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | | `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md index 232dbd9d5..5d8855925 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md @@ -129,7 +129,7 @@ function authorizeOperator( ) external nonpayable; ``` -Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). Notify the operator based on the LSP1-UniversalReceiver standard
@@ -528,8 +528,8 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP8CompatibleERC721Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.sol) -- Function signature: `revokeOperator(address,bytes32,bytes)` -- Function selector: `0xf1b97e04` +- Function signature: `revokeOperator(address,bytes32,bool,bytes)` +- Function selector: `0xdb8c9663` ::: @@ -537,6 +537,7 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -545,11 +546,12 @@ Remove access of `operator` for a given `tokenId`, disallowing it to transfer `t #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ---------------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -1170,6 +1172,7 @@ function _revokeOperator( address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1589,13 +1592,13 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP8CompatibleERC721Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.sol) -- Event signature: `RevokedOperator(address,address,bytes32,bytes)` -- Event topic hash: `0x501bc920d7f604417e315bcf29247652b2327fa1076b27b7f132bd8927cb15ea` +- Event signature: `RevokedOperator(address,address,bytes32,bool,bytes)` +- Event topic hash: `0x3ee932cea40ebbbfd8577d47156cc17cce8683802c57bbd1fb8c131c6f07af0a` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. @@ -1607,6 +1610,7 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i | `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. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | | `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md index 8f61176f7..23b96a06a 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md @@ -125,7 +125,7 @@ function authorizeOperator( ) external nonpayable; ``` -Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). +Allow an `operator` address to transfer or burn a specific `tokenId` on behalf of its token owner. See [`isOperatorFor`](#isoperatorfor). Notify the operator based on the LSP1-UniversalReceiver standard #### Parameters @@ -387,8 +387,8 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokeoperator) - Solidity implementation: [`LSP8Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.sol) -- Function signature: `revokeOperator(address,bytes32,bytes)` -- Function selector: `0xf1b97e04` +- Function signature: `revokeOperator(address,bytes32,bool,bytes)` +- Function selector: `0xdb8c9663` ::: @@ -396,6 +396,7 @@ Leaves the contract without owner. It will not be possible to call `onlyOwner` f function revokeOperator( address operator, bytes32 tokenId, + bool notify, bytes operatorNotificationData ) external nonpayable; ``` @@ -404,11 +405,12 @@ Remove access of `operator` for a given `tokenId`, disallowing it to transfer `t #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | ---------------------------------------------------- | -| `operator` | `address` | The address to revoke as an operator. | -| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | -------------------------------------------------------- | +| `operator` | `address` | The address to revoke as an operator. | +| `tokenId` | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notify` | `bool` | Boolean indicating whether to notify the operator or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
@@ -816,6 +818,7 @@ function _revokeOperator( address operator, address tokenOwner, bytes32 tokenId, + bool notified, bytes operatorNotificationData ) internal nonpayable; ``` @@ -1247,13 +1250,13 @@ event OwnershipTransferred(address indexed previousOwner, address indexed newOwn - Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#revokedoperator) - Solidity implementation: [`LSP8Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.sol) -- Event signature: `RevokedOperator(address,address,bytes32,bytes)` -- Event topic hash: `0x501bc920d7f604417e315bcf29247652b2327fa1076b27b7f132bd8927cb15ea` +- Event signature: `RevokedOperator(address,address,bytes32,bool,bytes)` +- Event topic hash: `0x3ee932cea40ebbbfd8577d47156cc17cce8683802c57bbd1fb8c131c6f07af0a` ::: ```solidity -event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bytes operatorNotificationData); +event RevokedOperator(address indexed operator, address indexed tokenOwner, bytes32 indexed tokenId, bool notified, bytes operatorNotificationData); ``` Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on its behalf. @@ -1265,6 +1268,7 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i | `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. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | | `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts b/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts index c26d66cba..52d54439a 100644 --- a/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts +++ b/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts @@ -1331,7 +1331,7 @@ export const shouldBehaveLikeLSP1Delegate = ( .connect(context.accounts.owner1) .setData( ERC725YDataKeys.LSP5.LSP5ReceivedAssetsMap + token.address.substring(2), - '0x0551951200000000000000000000000000000000cafecafe', + '0xdaa746b700000000000000000000000000000000cafecafe', ); expect( @@ -1339,7 +1339,7 @@ export const shouldBehaveLikeLSP1Delegate = ( ).to.deep.equal([ '0x' + '00'.repeat(15) + '01', token.address.toLowerCase(), - '0x0551951200000000000000000000000000000000cafecafe', + '0xdaa746b700000000000000000000000000000000cafecafe', ]); balance = await token.balanceOf(context.universalProfile1.address); @@ -1400,7 +1400,7 @@ export const shouldBehaveLikeLSP1Delegate = ( ).to.deep.equal([ '0x' + '00'.repeat(15) + '01', token.address.toLowerCase(), - '0x0551951200000000000000000000000000000000cafecafe', + '0xdaa746b700000000000000000000000000000000cafecafe', ]); }); }); diff --git a/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault.behaviour.ts b/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault.behaviour.ts index 727852c19..b83a6c871 100644 --- a/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault.behaviour.ts +++ b/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault.behaviour.ts @@ -980,7 +980,7 @@ export const shouldBehaveLikeLSP1Delegate = (buildContext: () => Promise Promise Promise { - const operatorThatConsumeAllGas: UniversalReceiverDelegateGasConsumer = - await new UniversalReceiverDelegateGasConsumer__factory( - context.accounts.owner, - ).deploy(); - const operator = operatorThatConsumeAllGas.address; - const tokenOwner = context.accounts.owner.address; - const amount = 1; - - const tx = await context.lsp7CompatibleERC20.approve(operator, amount); - - await expect(tx) - .to.emit(context.lsp7CompatibleERC20, 'AuthorizedOperator') - .withArgs(operator, tokenOwner, amount, '0x'); - - expect( - await context.lsp7CompatibleERC20.authorizedAmountFor(operator, tokenOwner), - ).to.equal(amount); - }); }); }); @@ -203,7 +183,7 @@ export const shouldBehaveLikeLSP7CompatibleERC20 = ( await expect(tx) .to.emit(context.lsp7CompatibleERC20, 'RevokedOperator') - .withArgs(operator, tokenOwner, '0x'); + .withArgs(operator, tokenOwner, false, '0x'); await expect(tx) .to.emit(context.lsp7CompatibleERC20, 'Approval') @@ -214,7 +194,7 @@ export const shouldBehaveLikeLSP7CompatibleERC20 = ( }); describe('changing the allowance of an LSP1 contract to zero', () => { - it('should succeed and inform the operator', async () => { + it('should succeed and not inform the operator', async () => { const tokenReceiverWithLSP1: TokenReceiverWithLSP1 = await new TokenReceiverWithLSP1__factory(context.accounts.owner).deploy(); const operator = tokenReceiverWithLSP1.address; @@ -226,9 +206,9 @@ export const shouldBehaveLikeLSP7CompatibleERC20 = ( await expect(tx) .to.emit(context.lsp7CompatibleERC20, 'RevokedOperator') - .withArgs(operator, tokenOwner, '0x'); + .withArgs(operator, tokenOwner, false, '0x'); - await expect(tx).to.emit(tokenReceiverWithLSP1, 'UniversalReceiver'); + expect(tx).to.not.emit(tokenReceiverWithLSP1, 'UniversalReceiver'); expect( await context.lsp7CompatibleERC20.authorizedAmountFor(operator, tokenOwner), @@ -245,26 +225,7 @@ export const shouldBehaveLikeLSP7CompatibleERC20 = ( await expect(tx) .to.emit(context.lsp7CompatibleERC20, 'RevokedOperator') - .withArgs(operator, tokenOwner, '0x'); - - expect( - await context.lsp7CompatibleERC20.authorizedAmountFor(operator, tokenOwner), - ).to.equal(ethers.constants.Zero); - }); - - it.skip('should succeed and inform the operator even if the operator use gas indefinitely', async () => { - const operatorThatConsumeAllGas: UniversalReceiverDelegateGasConsumer = - await new UniversalReceiverDelegateGasConsumer__factory( - context.accounts.owner, - ).deploy(); - const operator = operatorThatConsumeAllGas.address; - const tokenOwner = context.accounts.owner.address; - - const tx = await context.lsp7CompatibleERC20.approve(operator, 0); - - await expect(tx) - .to.emit(context.lsp7CompatibleERC20, 'RevokedOperator') - .withArgs(operator, tokenOwner, '0x'); + .withArgs(operator, tokenOwner, false, '0x'); expect( await context.lsp7CompatibleERC20.authorizedAmountFor(operator, tokenOwner), diff --git a/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts b/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts index 0d41e3c03..3e83f6aa9 100644 --- a/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts +++ b/tests/LSP7DigitalAsset/LSP7DigitalAsset.behaviour.ts @@ -14,8 +14,8 @@ import { TokenReceiverWithoutLSP1__factory, UniversalReceiverDelegateRevert, UniversalReceiverDelegateRevert__factory, - UniversalReceiverDelegateGasConsumer, - UniversalReceiverDelegateGasConsumer__factory, + TokenReceiverWithLSP1Revert, + TokenReceiverWithLSP1Revert__factory, } from '../../types'; // constants @@ -297,24 +297,6 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise { - const operatorThatConsumeAllGas: UniversalReceiverDelegateGasConsumer = - await new UniversalReceiverDelegateGasConsumer__factory( - context.accounts.owner, - ).deploy(); - const operator = operatorThatConsumeAllGas.address; - const tokenOwner = context.accounts.owner.address; - const amount = context.initialSupply; - - const tx = await context.lsp7.authorizeOperator(operator, amount, '0xaabbccdd'); - - await expect(tx) - .to.emit(context.lsp7, 'AuthorizedOperator') - .withArgs(operator, tokenOwner, amount, '0xaabbccdd'); - - expect(await context.lsp7.authorizedAmountFor(operator, tokenOwner)).to.equal(amount); - }); }); }); @@ -343,25 +325,15 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise { - it('should authorize for the `addedAmount` and add the operator to the list of operators', async () => { - const oldOperator = context.accounts.operator.address; + it('should revert', async () => { const newOperator = context.accounts.anyone.address; - const tokenOwner = context.accounts.owner.address; - const tx = await context.lsp7.increaseAllowance(newOperator, addedAmount, '0x'); - - await expect(tx) - .to.emit(context.lsp7, 'AuthorizedOperator') - .withArgs(newOperator, tokenOwner, addedAmount, '0x'); - - expect(await context.lsp7.authorizedAmountFor(newOperator, tokenOwner)).to.equal( - addedAmount, - ); - - expect(await context.lsp7.getOperatorsOf(tokenOwner)).to.deep.equal([ - oldOperator, - newOperator, - ]); + await expect(context.lsp7.increaseAllowance(newOperator, addedAmount, '0x')) + .to.be.revertedWithCustomError( + context.lsp7, + 'OperatorAllowanceCannotBeIncreasedFromZero', + ) + .withArgs(newOperator); }); }); @@ -404,23 +376,18 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise { - it('should authorize for the `addedAmount`', async () => { + it('should revert', async () => { const operator = context.accounts.anyone.address; const tokenOwner = context.accounts.owner.address; - const tx = await context.lsp7.increaseAllowance( - operator, - addedAmountLargerThanBalance, - '0x', - ); - - await expect(tx) - .to.emit(context.lsp7, 'AuthorizedOperator') - .withArgs(operator, tokenOwner, addedAmountLargerThanBalance, '0x'); - - expect(await context.lsp7.authorizedAmountFor(operator, tokenOwner)).to.equal( - addedAmountLargerThanBalance, - ); + await expect( + context.lsp7.increaseAllowance(operator, addedAmountLargerThanBalance, '0x'), + ) + .to.be.revertedWithCustomError( + context.lsp7, + 'OperatorAllowanceCannotBeIncreasedFromZero', + ) + .withArgs(operator); }); }); @@ -450,16 +417,6 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise { - it('should revert', async () => { - const addedAmount = ethers.BigNumber.from('1'); - - await expect( - context.lsp7.increaseAllowance(ethers.constants.AddressZero, addedAmount, '0x'), - ).to.be.revertedWithCustomError(context.lsp7, 'LSP7CannotUseAddressZeroAsOperator'); - }); - }); - describe('when `operator` param is `msg.sender`', () => { it('should revert', async () => { const addedAmount = ethers.BigNumber.from('1'); @@ -553,7 +510,7 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise Promise Promise Promise { const operator = ethers.constants.AddressZero; - await expect(context.lsp7.revokeOperator(operator, '0x')).to.be.revertedWithCustomError( - context.lsp7, - 'LSP7CannotUseAddressZeroAsOperator', - ); + await expect( + context.lsp7.revokeOperator(operator, false, '0x'), + ).to.be.revertedWithCustomError(context.lsp7, 'LSP7CannotUseAddressZeroAsOperator'); }); }); @@ -653,10 +609,9 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise { const operator = context.accounts.owner.address; - await expect(context.lsp7.revokeOperator(operator, '0x')).to.be.revertedWithCustomError( - context.lsp7, - 'LSP7TokenOwnerCannotBeOperator', - ); + await expect( + context.lsp7.revokeOperator(operator, false, '0x'), + ).to.be.revertedWithCustomError(context.lsp7, 'LSP7TokenOwnerCannotBeOperator'); }); }); @@ -667,11 +622,13 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise Promise { - const operatorThatReverts: UniversalReceiverDelegateRevert = - await new UniversalReceiverDelegateRevert__factory(context.accounts.owner).deploy(); + it('should inform the operator and revert when the operator universalReceiver revert', async () => { + const operatorThatReverts: TokenReceiverWithLSP1Revert = + await new TokenReceiverWithLSP1Revert__factory(context.accounts.owner).deploy(); const operator = operatorThatReverts.address; const tokenOwner = context.accounts.owner.address; - const tx = await context.lsp7.revokeOperator(operator, '0xaabbccdd'); + await context.lsp7.authorizeOperator(operator, 1, '0x'); - await expect(tx) - .to.emit(context.lsp7, 'RevokedOperator') - .withArgs(operator, tokenOwner, '0xaabbccdd'); + await operatorThatReverts.addLSP1Support(); - expect(await context.lsp7.authorizedAmountFor(operator, tokenOwner)).to.equal( - ethers.constants.Zero, + await expect(context.lsp7.revokeOperator(operator, true, '0xaabbccdd')).to.be.revertedWith( + 'I reverted', ); }); - it.skip('should succeed and inform the operator even if the operator use gas indefinitely', async () => { - const operatorThatConsumeAllGas: UniversalReceiverDelegateGasConsumer = - await new UniversalReceiverDelegateGasConsumer__factory(context.accounts.owner).deploy(); - const operator = operatorThatConsumeAllGas.address; + it('should inform the operator and revert when the operator universalReceiver revert', async () => { + const operatorThatReverts: TokenReceiverWithLSP1Revert = + await new TokenReceiverWithLSP1Revert__factory(context.accounts.owner).deploy(); + const operator = operatorThatReverts.address; const tokenOwner = context.accounts.owner.address; - const tx = await context.lsp7.revokeOperator(operator, '0xaabbccdd'); + await context.lsp7.authorizeOperator(operator, 1, '0x'); - await expect(tx) - .to.emit(context.lsp7, 'RevokedOperator') - .withArgs(operator, tokenOwner, '0xaabbccdd'); + await operatorThatReverts.addLSP1Support(); - expect(await context.lsp7.authorizedAmountFor(operator, tokenOwner)).to.equal( - ethers.constants.Zero, + await expect(context.lsp7.revokeOperator(operator, false, '0xaabbccdd')).to.emit( + context.lsp7, + 'RevokedOperator', ); }); }); @@ -858,7 +812,7 @@ export const shouldBehaveLikeLSP7 = (buildContext: () => Promise Promise Promise { - const operatorThatConsumeAllGas: UniversalReceiverDelegateGasConsumer = - await new UniversalReceiverDelegateGasConsumer__factory( - context.accounts.owner, - ).deploy(); - const operator = operatorThatConsumeAllGas.address; - const tokenOwner = context.accounts.owner.address; - const tokenId = mintedTokenId; - - const tx = await context.lsp8CompatibleERC721.approve(operator, tokenId); - - await expect(tx) - .to.emit(context.lsp8CompatibleERC721, 'AuthorizedOperator') - .withArgs(operator, tokenOwner, tokenIdAsBytes32(tokenId), '0x'); - - expect( - await context.lsp8CompatibleERC721.isOperatorFor( - operator, - tokenIdAsBytes32(tokenId), - ), - ).to.be.true; - }); }); }); @@ -774,7 +751,7 @@ export const shouldBehaveLikeLSP8CompatibleERC721 = ( await expect(tx) .to.emit(context.lsp8CompatibleERC721, 'RevokedOperator') - .withArgs(context.accounts.operator.address, from, tokenIdAsBytes32(tokenId), '0x'); + .withArgs(context.accounts.operator.address, from, tokenIdAsBytes32(tokenId), false, '0x'); // post-conditions const postOwnerOf = await context.lsp8CompatibleERC721.ownerOf(tokenId); diff --git a/tests/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.behaviour.ts b/tests/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.behaviour.ts index b0508f91f..7b1efc978 100644 --- a/tests/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.behaviour.ts +++ b/tests/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.behaviour.ts @@ -16,6 +16,8 @@ import { UniversalReceiverDelegateRevert__factory, UniversalReceiverDelegateGasConsumer, UniversalReceiverDelegateGasConsumer__factory, + TokenReceiverWithLSP1Revert, + TokenReceiverWithLSP1Revert__factory, } from '../../types'; // helpers @@ -326,24 +328,6 @@ export const shouldBehaveLikeLSP8 = ( expect(await context.lsp8.isOperatorFor(operator, tokenId)).to.be.true; }); - - it.skip('should succeed and inform the operator even if the operator use gas indefinitely', async () => { - const operatorThatConsumeAllGas: UniversalReceiverDelegateGasConsumer = - await new UniversalReceiverDelegateGasConsumer__factory( - context.accounts.owner, - ).deploy(); - const operator = operatorThatConsumeAllGas.address; - const tokenOwner = context.accounts.owner.address; - const tokenId = newMintedTokenId; - - const tx = await context.lsp8.authorizeOperator(operator, tokenId, '0xaabbccdd'); - - await expect(tx) - .to.emit(context.lsp8, 'AuthorizedOperator') - .withArgs(operator, tokenOwner, tokenId, '0xaabbccdd'); - - expect(await context.lsp8.isOperatorFor(operator, tokenId)).to.be.true; - }); }); }); }); @@ -353,7 +337,12 @@ export const shouldBehaveLikeLSP8 = ( describe('when tokenId does not exist', () => { it('should revert', async () => { await expect( - context.lsp8.revokeOperator(context.accounts.operator.address, neverMintedTokenId, '0x'), + context.lsp8.revokeOperator( + context.accounts.operator.address, + neverMintedTokenId, + false, + '0x', + ), ) .to.be.revertedWithCustomError(context.lsp8, 'LSP8NonExistentTokenId') .withArgs(neverMintedTokenId); @@ -377,7 +366,7 @@ export const shouldBehaveLikeLSP8 = ( await expect( context.lsp8 .connect(context.accounts.anyone) - .revokeOperator(context.accounts.operator.address, mintedTokenId, '0x'), + .revokeOperator(context.accounts.operator.address, mintedTokenId, false, '0x'), ) .to.be.revertedWithCustomError(context.lsp8, 'LSP8NotTokenOwner') .withArgs(context.accounts.owner.address, mintedTokenId, context.accounts.anyone.address); @@ -395,10 +384,10 @@ export const shouldBehaveLikeLSP8 = ( expect(await context.lsp8.isOperatorFor(operator, tokenId)).to.be.true; // effects - const tx = await context.lsp8.revokeOperator(operator, tokenId, '0x'); + const tx = await context.lsp8.revokeOperator(operator, tokenId, false, '0x'); await expect(tx) .to.emit(context.lsp8, 'RevokedOperator') - .withArgs(operator, tokenOwner, tokenId, '0x'); + .withArgs(operator, tokenOwner, tokenId, false, '0x'); // post-conditions expect(await context.lsp8.isOperatorFor(operator, tokenId)).to.be.false; @@ -411,7 +400,7 @@ export const shouldBehaveLikeLSP8 = ( const tokenId = mintedTokenId; await expect( - context.lsp8.revokeOperator(operator, tokenId, '0x'), + context.lsp8.revokeOperator(operator, tokenId, false, '0x'), ).to.be.revertedWithCustomError(context.lsp8, 'LSP8CannotUseAddressZeroAsOperator'); }); }); @@ -421,7 +410,7 @@ export const shouldBehaveLikeLSP8 = ( const operator = context.accounts.anyone.address; const tokenId = mintedTokenId; - await expect(context.lsp8.revokeOperator(operator, tokenId, '0x')) + await expect(context.lsp8.revokeOperator(operator, tokenId, false, '0x')) .to.be.revertedWithCustomError(context.lsp8, 'LSP8NonExistingOperator') .withArgs(operator, tokenId); }); @@ -445,22 +434,22 @@ export const shouldBehaveLikeLSP8 = ( // pre-condition await context.lsp8.authorizeOperator(operator, tokenId, '0xaabbccdd'); - const tx = await context.lsp8.revokeOperator(operator, tokenId, '0xaabbccdd', { + const tx = await context.lsp8.revokeOperator(operator, tokenId, true, '0xaabbccdd', { gasLimit: 2000000, }); await expect(tx) .to.emit(context.lsp8, 'RevokedOperator') - .withArgs(operator, tokenOwner, tokenId, '0xaabbccdd'); + .withArgs(operator, tokenOwner, tokenId, true, '0xaabbccdd'); await expect(tx).to.emit(tokenReceiverWithLSP1, 'UniversalReceiver'); expect(await context.lsp8.isOperatorFor(operator, tokenId)).to.be.false; }); - it('should succeed and inform the operator even if the operator revert', async () => { - const operatorThatReverts: UniversalReceiverDelegateRevert = - await new UniversalReceiverDelegateRevert__factory(context.accounts.owner).deploy(); + it('should inform the operator and revert when the operator revert', async () => { + const operatorThatReverts: TokenReceiverWithLSP1Revert = + await new TokenReceiverWithLSP1Revert__factory(context.accounts.owner).deploy(); const operator = operatorThatReverts.address; const tokenOwner = context.accounts.owner.address; const tokenId = newMintedTokenId; @@ -468,34 +457,11 @@ export const shouldBehaveLikeLSP8 = ( // pre-condition await context.lsp8.authorizeOperator(operator, tokenId, '0xaabbccdd'); - const tx = await context.lsp8.revokeOperator(operator, tokenId, '0xaabbccdd'); + await operatorThatReverts.addLSP1Support(); - await expect(tx) - .to.emit(context.lsp8, 'RevokedOperator') - .withArgs(operator, tokenOwner, tokenId, '0xaabbccdd'); - - expect(await context.lsp8.isOperatorFor(operator, tokenId)).to.be.false; - }); - - it.skip('should succeed and inform the operator even if the operator use gas indefinitely', async () => { - const operatorThatConsumeAllGas: UniversalReceiverDelegateGasConsumer = - await new UniversalReceiverDelegateGasConsumer__factory( - context.accounts.owner, - ).deploy(); - const operator = operatorThatConsumeAllGas.address; - const tokenOwner = context.accounts.owner.address; - const tokenId = newMintedTokenId; - - // pre-condition - await context.lsp8.authorizeOperator(operator, tokenId, '0xaabbccdd'); - - const tx = await context.lsp8.revokeOperator(operator, tokenId, '0xaabbccdd'); - - await expect(tx) - .to.emit(context.lsp8, 'RevokedOperator') - .withArgs(operator, tokenOwner, tokenId, '0xaabbccdd'); - - expect(await context.lsp8.isOperatorFor(operator, tokenId)).to.be.false; + await expect( + context.lsp8.revokeOperator(operator, tokenId, true, '0xaabbccdd'), + ).to.be.revertedWith('I reverted'); }); }); }); @@ -565,22 +531,34 @@ export const shouldBehaveLikeLSP8 = ( describe('when tokenId has been minted', () => { after('cleanup operators', async () => { - await context.lsp8.revokeOperator(context.accounts.operator.address, mintedTokenId, '0x'); + await context.lsp8.revokeOperator( + context.accounts.operator.address, + mintedTokenId, + false, + '0x', + ); await context.lsp8.revokeOperator( context.accounts.anotherOperator.address, mintedTokenId, + false, '0x', ); }); describe('when operator has not been authorized', () => { before('remove operators', async () => { - await context.lsp8.revokeOperator(context.accounts.operator.address, mintedTokenId, '0x'); + await context.lsp8.revokeOperator( + context.accounts.operator.address, + mintedTokenId, + false, + '0x', + ); await context.lsp8.revokeOperator( context.accounts.anotherOperator.address, mintedTokenId, + false, '0x', ); }); @@ -703,11 +681,11 @@ export const shouldBehaveLikeLSP8 = ( await expect(tx) .to.emit(context.lsp8, 'RevokedOperator') - .withArgs(context.accounts.operator.address, from, tokenId, '0x'); + .withArgs(context.accounts.operator.address, from, tokenId, false, '0x'); await expect(tx) .to.emit(context.lsp8, 'RevokedOperator') - .withArgs(context.accounts.anotherOperator.address, from, tokenId, '0x'); + .withArgs(context.accounts.anotherOperator.address, from, tokenId, false, '0x'); // post-conditions const postTokenOwnerOf = await context.lsp8.tokenOwnerOf(tokenId); @@ -1125,13 +1103,20 @@ export const shouldBehaveLikeLSP8 = ( ); await expect(tx) .to.emit(context.lsp8, 'RevokedOperator') - .withArgs(context.accounts.operator.address, from[index], tokenId[index], '0x'); + .withArgs( + context.accounts.operator.address, + from[index], + tokenId[index], + false, + '0x', + ); await expect(tx) .to.emit(context.lsp8, 'RevokedOperator') .withArgs( context.accounts.anotherOperator.address, from[index], tokenId[index], + false, '0x', ); }),