Skip to content

Commit

Permalink
feat!: allow Operator to revoke himself (#893)
Browse files Browse the repository at this point in the history
* feat!: allow operator to revoke himself

* test: add test for operator revoking himself

* refactor!: change LSP7 interfaceId

* chore: fix parameter and generate natspec

* chore: do suggested changes

---------

Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com>
  • Loading branch information
YamenMerhi and CJ42 committed Feb 28, 2024
1 parent 671c4cd commit f4b21eb
Show file tree
Hide file tree
Showing 19 changed files with 657 additions and 291 deletions.
2 changes: 1 addition & 1 deletion constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const INTERFACE_IDS = {
LSP1UniversalReceiver: '0x6bb56a14',
LSP1UniversalReceiverDelegate: '0xa245bbda',
LSP6KeyManager: '0x23f34c62',
LSP7DigitalAsset: '0xb3c4928f',
LSP7DigitalAsset: '0xc52d6008',
LSP8IdentifiableDigitalAsset: '0x3a271706',
LSP9Vault: '0x28af17e6',
LSP11BasicSocialRecovery: '0x049a28f1',
Expand Down
10 changes: 7 additions & 3 deletions contracts/LSP7DigitalAsset/ILSP7DigitalAsset.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,21 +122,23 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
) external;

/**
* @dev Removes the `operator` address as an operator of callers tokens, disallowing it to send any amount of tokens
* on behalf of the token owner (the caller of the function `msg.sender`). See also {authorizedAmountFor}.
* @dev Enables `tokenOwner` to remove `operator` for its tokens, disallowing it to send any amount of tokens on its behalf.
* This function also allows the `operator` to remove itself if it is the caller of this function
*
* @param operator The address to revoke as an operator.
* @param tokenOwner The address of the token owner.
* @param notify Boolean indicating whether to notify the operator or not.
* @param operatorNotificationData The data to notify the operator about via LSP1.
*
* @custom:requirements
* - `operator` cannot be calling address.
* - caller MUST be `operator` or `tokenOwner`
* - `operator` cannot be the zero address.
*
* @custom:events {OperatorRevoked} event with address of the operator being revoked for the caller (token holder).
*/
function revokeOperator(
address operator,
address tokenOwner,
bool notify,
bytes memory operatorNotificationData
) external;
Expand Down Expand Up @@ -184,6 +186,7 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
* indicating `operator` does not have any alauthorizedAmountForlowance left for `msg.sender`.
*
* @param operator The operator to decrease allowance for `msg.sender`
* @param tokenOwner The address of the token owner.
* @param subtractedAmount The amount to decrease by in the operator's allowance.
*
* @custom:requirements
Expand All @@ -192,6 +195,7 @@ interface ILSP7DigitalAsset is IERC165, IERC725Y {
*/
function decreaseAllowance(
address operator,
address tokenOwner,
uint256 subtractedAmount,
bytes memory operatorNotificationData
) external;
Expand Down
2 changes: 1 addition & 1 deletion contracts/LSP7DigitalAsset/LSP7Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.4;

// --- ERC165 interface ids
bytes4 constant _INTERFACEID_LSP7 = 0xb3c4928f;
bytes4 constant _INTERFACEID_LSP7 = 0xc52d6008;

// --- Token Hooks

Expand Down
35 changes: 26 additions & 9 deletions contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {LSP1Utils} from "../LSP1UniversalReceiver/LSP1Utils.sol";

// errors
import {
LSP7CannotSendToSelf,
LSP7AmountExceedsAuthorizedAmount,
LSP7InvalidTransferBatch,
LSP7AmountExceedsBalance,
Expand All @@ -31,7 +30,9 @@ import {
LSP7NotifyTokenReceiverContractMissingLSP1Interface,
LSP7NotifyTokenReceiverIsEOA,
OperatorAllowanceCannotBeIncreasedFromZero,
LSP7BatchCallFailed
LSP7BatchCallFailed,
LSP7RevokeOperatorNotAuthorized,
LSP7DecreaseAllowanceNotAuthorized
} from "./LSP7Errors.sol";

// constants
Expand Down Expand Up @@ -173,11 +174,20 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
*/
function revokeOperator(
address operator,
address tokenOwner,
bool notify,
bytes memory operatorNotificationData
) public virtual override {
if (msg.sender != tokenOwner && msg.sender != operator) {
revert LSP7RevokeOperatorNotAuthorized(
msg.sender,
tokenOwner,
operator
);
}

_updateOperator(
msg.sender,
tokenOwner,
operator,
0,
notify,
Expand All @@ -186,7 +196,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {

if (notify) {
bytes memory lsp1Data = abi.encode(
msg.sender,
tokenOwner,
0,
operatorNotificationData
);
Expand Down Expand Up @@ -254,10 +264,19 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
*/
function decreaseAllowance(
address operator,
address tokenOwner,
uint256 subtractedAmount,
bytes memory operatorNotificationData
) public virtual override {
uint256 currentAllowance = authorizedAmountFor(operator, msg.sender);
if (msg.sender != tokenOwner && msg.sender != operator) {
revert LSP7DecreaseAllowanceNotAuthorized(
msg.sender,
tokenOwner,
operator
);
}

uint256 currentAllowance = authorizedAmountFor(operator, tokenOwner);
if (currentAllowance < subtractedAmount) {
revert LSP7DecreasedAllowanceBelowZero();
}
Expand All @@ -266,7 +285,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
unchecked {
newAllowance = currentAllowance - subtractedAmount;
_updateOperator(
msg.sender,
tokenOwner,
operator,
newAllowance,
true,
Expand All @@ -275,7 +294,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
}

bytes memory lsp1Data = abi.encode(
msg.sender,
tokenOwner,
newAllowance,
operatorNotificationData
);
Expand All @@ -295,8 +314,6 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset {
bool force,
bytes memory data
) public virtual override {
if (from == to) revert LSP7CannotSendToSelf();

if (msg.sender != from) {
_spendAllowance({
operator: msg.sender,
Expand Down
23 changes: 18 additions & 5 deletions contracts/LSP7DigitalAsset/LSP7Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ error LSP7CannotUseAddressZeroAsOperator();
*/
error LSP7CannotSendWithAddressZero();

/**
* @dev reverts when specifying the same address for `from` or `to` in a token transfer.
*/
error LSP7CannotSendToSelf();

/**
* @dev reverts when the array parameters used in {transferBatch} have different lengths.
*/
Expand Down Expand Up @@ -88,3 +83,21 @@ error OperatorAllowanceCannotBeIncreasedFromZero(address operator);
* @notice Batch call failed.
*/
error LSP7BatchCallFailed(uint256 callIndex);

/**
* @dev Reverts when the call to revoke operator is not authorized.
*/
error LSP7RevokeOperatorNotAuthorized(
address caller,
address tokenOwner,
address operator
);

/**
* @dev Reverts when the call to decrease allowance is not authorized.
*/
error LSP7DecreaseAllowanceNotAuthorized(
address caller,
address tokenOwner,
address operator
);
14 changes: 9 additions & 5 deletions contracts/LSP8IdentifiableDigitalAsset/LSP8Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ error LSP8CannotUseAddressZeroAsOperator();
*/
error LSP8CannotSendToAddressZero();

/**
* @dev Reverts when specifying the same address for `from` and `to` in a token transfer.
*/
error LSP8CannotSendToSelf();

/**
* @dev Reverts when `operator` is not an operator for the `tokenId`.
*/
Expand Down Expand Up @@ -112,3 +107,12 @@ error LSP8TokenOwnerChanged(
address oldOwner,
address newOwner
);

/**
* @dev Reverts when the call to revoke operator is not authorized.
*/
error LSP8RevokeOperatorNotAuthorized(
address caller,
address tokenOwner,
bytes32 tokenId
);
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ import {
LSP8NonExistingOperator,
LSP8CannotSendToAddressZero,
LSP8TokenIdAlreadyMinted,
LSP8CannotSendToSelf,
LSP8NotifyTokenReceiverContractMissingLSP1Interface,
LSP8NotifyTokenReceiverIsEOA,
LSP8TokenIdsDataLengthMismatch,
LSP8TokenIdsDataEmptyArray,
LSP8BatchCallFailed,
LSP8TokenOwnerChanged
LSP8TokenOwnerChanged,
LSP8RevokeOperatorNotAuthorized
} from "./LSP8Errors.sol";

// constants
Expand Down Expand Up @@ -293,8 +293,14 @@ abstract contract LSP8IdentifiableDigitalAssetCore is
) public virtual override {
address tokenOwner = tokenOwnerOf(tokenId);

if (tokenOwner != msg.sender) {
revert LSP8NotTokenOwner(tokenOwner, tokenId, msg.sender);
if (msg.sender != tokenOwner) {
if (operator != msg.sender) {
revert LSP8RevokeOperatorNotAuthorized(
msg.sender,
tokenOwner,
tokenId
);
}
}

if (operator == address(0)) {
Expand All @@ -315,7 +321,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is

if (notify) {
bytes memory lsp1Data = abi.encode(
msg.sender,
tokenOwner,
tokenId,
false, // unauthorized
operatorNotificationData
Expand Down Expand Up @@ -625,10 +631,6 @@ abstract contract LSP8IdentifiableDigitalAssetCore is
bool force,
bytes memory data
) internal virtual {
if (from == to) {
revert LSP8CannotSendToSelf();
}

address tokenOwner = tokenOwnerOf(tokenId);
if (tokenOwner != from) {
revert LSP8NotTokenOwner(tokenOwner, tokenId, from);
Expand Down
2 changes: 1 addition & 1 deletion docs/_interface_ids_table.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
| **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** | `0xb3c4928f` | Interface of the LSP7 - Digital Asset standard, a fungible digital asset. |
| **LSP7DigitalAsset** | `0xc52d6008` | Interface of the LSP7 - Digital Asset standard, a fungible digital asset. |
| **LSP8IdentifiableDigitalAsset** | `0x3a271706` | 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. |
Expand Down
Loading

0 comments on commit f4b21eb

Please sign in to comment.