-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* initial commit * forge install: forge-std v1.5.2 * forge install: openzeppelin-contracts v4.8.2 * removed unused files and libraries * format code * Extend testing --------- Co-authored-by: Crisgarner <@crisgarner> Co-authored-by: cxkoda <[email protected]>
- Loading branch information
1 parent
edc7d7a
commit c0d4b51
Showing
11 changed files
with
888 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Compiler files | ||
cache/ | ||
out/ | ||
|
||
# Ignores development broadcast logs | ||
!/broadcast | ||
/broadcast/*/31337/ | ||
/broadcast/**/dry-run/ | ||
|
||
# Env file | ||
.env | ||
|
||
# Editor files | ||
.gitpod.yml | ||
.DS_Store | ||
/.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[submodule "lib/forge-std"] | ||
path = lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std | ||
branch = v1.5.2 | ||
[submodule "lib/openzeppelin-contracts"] | ||
path = lib/openzeppelin-contracts | ||
url = https://github.com/Openzeppelin/openzeppelin-contracts | ||
branch = v4.8.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,11 @@ | ||
# ERC-6464 | ||
# ERC6464 Reference Implementations | ||
|
||
This is the source code for a reference implementation of ERC6464. | ||
|
||
## Build and Test | ||
|
||
The repo expects [Foundry](https://github.com/foundry-rs/foundry/tree/master/forge) to be installed: | ||
|
||
```bash | ||
forge test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[profile.default] | ||
src = 'src' | ||
out = 'out' | ||
libs = ['lib'] | ||
|
||
[fmt] | ||
line_length = 120 | ||
single_line_statement_blocks = "multi" | ||
|
||
runs = 1024 | ||
max_test_rejects = 16777216 | ||
|
||
# See more config options https://github.com/foundry-rs/foundry/tree/master/config |
Submodule openzeppelin-contracts
added at
d00ace
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
ds-test/=lib/forge-std/lib/ds-test/src/ | ||
forge-std/=lib/forge-std/src/ | ||
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Copyright 2023 PROOF Holdings Inc | ||
pragma solidity ^0.8.0; | ||
|
||
import {IERC6464, IERC6464Events, IERC6464AnyApproval} from "./interfaces/IERC6464.sol"; | ||
import {IERC721, ERC721} from "openzeppelin-contracts/token/ERC721/ERC721.sol"; | ||
|
||
abstract contract ERC6464 is ERC721, IERC6464, IERC6464AnyApproval, IERC6464Events { | ||
type TokenNonce is uint256; | ||
type OwnerNonce is uint256; | ||
|
||
/** | ||
* @notice Thrown if a caller is not authorized (owner or approved) to perform an action. | ||
*/ | ||
error NotAuthorized(address operator, uint256 tokenId); | ||
|
||
/** | ||
* @notice Nonce used to efficiently revoke all approvals of a tokenId | ||
*/ | ||
mapping(uint256 => TokenNonce) private _tokenNonce; | ||
/** | ||
* @notice Nonce used to efficiently revoke all approvals of an Owner | ||
*/ | ||
mapping(address => OwnerNonce) private _ownerNonce; | ||
|
||
/** | ||
* @dev tokenId -> tokenNonce -> ownerNounce -> operator -> approval | ||
*/ | ||
mapping(uint256 => mapping(TokenNonce => mapping(OwnerNonce => mapping(address => bool)))) private | ||
_isExplicitlyApprovedFor; | ||
|
||
/** | ||
* @inheritdoc IERC6464 | ||
*/ | ||
function setExplicitApproval(address operator, uint256 tokenId, bool approved) public { | ||
if (!_isApprovedOrOwner(_msgSender(), tokenId)) { | ||
revert NotAuthorized(_msgSender(), tokenId); | ||
} | ||
TokenNonce tNonce = _tokenNonce[tokenId]; | ||
OwnerNonce oNonce = _ownerNonce[ownerOf(tokenId)]; | ||
_isExplicitlyApprovedFor[tokenId][tNonce][oNonce][operator] = approved; | ||
emit ExplicitApprovalFor(operator, tokenId, approved); | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC6464 | ||
*/ | ||
function setExplicitApproval(address operator, uint256[] calldata tokenIds, bool approved) external { | ||
for (uint256 id = 0; id < tokenIds.length; id++) { | ||
setExplicitApproval(operator, tokenIds[id], approved); | ||
} | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC6464 | ||
*/ | ||
function revokeAllExplicitApprovals() external { | ||
_ownerNonce[_msgSender()] = OwnerNonce.wrap(OwnerNonce.unwrap(_ownerNonce[_msgSender()]) + 1); | ||
emit AllExplicitApprovalsRevoked(_msgSender()); | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC6464 | ||
*/ | ||
function revokeAllExplicitApprovals(uint256 tokenId) public { | ||
if (!_isApprovedOrOwner(_msgSender(), tokenId)) { | ||
revert NotAuthorized(_msgSender(), tokenId); | ||
} | ||
_revokeAllExplicitApprovals(tokenId); | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC6464 | ||
*/ | ||
function isExplicitlyApprovedFor(address operator, uint256 tokenId) public view returns (bool) { | ||
TokenNonce tNonce = _tokenNonce[tokenId]; | ||
OwnerNonce oNonce = _ownerNonce[ownerOf(tokenId)]; | ||
return _isExplicitlyApprovedFor[tokenId][tNonce][oNonce][operator]; | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC6464AnyApproval | ||
*/ | ||
function isApprovedFor(address operator, uint256 tokenId) public view returns (bool) { | ||
return isExplicitlyApprovedFor(operator, tokenId) || isApprovedForAll(ownerOf(tokenId), operator) | ||
|| getApproved(tokenId) == operator; | ||
} | ||
|
||
/** | ||
* @notice Revoking explicit approvals on token transfer. | ||
*/ | ||
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) | ||
internal | ||
virtual | ||
override | ||
{ | ||
super._beforeTokenTransfer(from, to, firstTokenId, batchSize); | ||
if (from == address(0)) { | ||
return; | ||
} | ||
|
||
for (uint256 i = 0; i < batchSize; i++) { | ||
_revokeAllExplicitApprovals(firstTokenId + i); | ||
} | ||
} | ||
|
||
/** | ||
* @notice Revokes all explicit approvals for a token. | ||
*/ | ||
function _revokeAllExplicitApprovals(uint256 tokenId) internal { | ||
_tokenNonce[tokenId] = TokenNonce.wrap(TokenNonce.unwrap(_tokenNonce[tokenId]) + 1); | ||
emit AllExplicitApprovalsRevoked(ownerOf(tokenId), tokenId); | ||
} | ||
|
||
/** | ||
* @notice Overriding OZ's `_isApprovedOrOwner` check to grant for explicit approvals the same permissions as standard | ||
* ERC721 approvals. | ||
*/ | ||
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual override returns (bool) { | ||
address owner = ownerOf(tokenId); | ||
return spender == owner || isApprovedFor(spender, tokenId); | ||
} | ||
|
||
/** | ||
* @notice OZ's `approve` does only check for `isApprovedForAll`. Overriding to allow all approvals. | ||
*/ | ||
function approve(address to, uint256 tokenId) public virtual override(ERC721, IERC721) { | ||
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: approve caller is not token owner or approved"); | ||
_approve(to, tokenId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// SPDX-License-Identifier: MIT | ||
// Copyright 2023 PROOF Holdings Inc | ||
pragma solidity ^0.8.0; | ||
|
||
// IERC721 is renamed to ERC721 for compatibility with the original EIP defition. | ||
import {IERC721 as ERC721} from "openzeppelin-contracts/token/ERC721/IERC721.sol"; | ||
|
||
/** | ||
* @notice Extends ERC-721 to include per-token approval for multiple operators. | ||
* @dev Off-chain indexers of approvals SHOULD assume that an operator is approved if either of `ERC721.Approval(…)` or | ||
* `ERC721.ApprovalForAll(…, true)` events are witnessed without the corresponding revocation(s), even if an | ||
* `ExplicitApprovalFor(…, false)` is emitted. | ||
* @dev TODO: the ERC-165 identifier for this interface is TBD. | ||
*/ | ||
interface IERC6464 is ERC721 { | ||
/** | ||
* @notice Approves the operator to manage the asset on behalf of its owner. | ||
* @dev Throws if `msg.sender` is not the current NFT owner, or an authorised operator of the current owner. | ||
* @dev Approvals set via this method MUST be revoked upon transfer of the token to a new owner; equivalent to | ||
* calling `revokeAllExplicitApprovals(tokenId)`, including associated events. | ||
* @dev MUST emit `ApprovalFor(operator, tokenId, approved)`. | ||
* @dev MUST NOT have an effect on any standard ERC721 approval setters / getters. | ||
*/ | ||
function setExplicitApproval(address operator, uint256 tokenId, bool approved) external; | ||
|
||
/** | ||
* @notice Approves the operator to manage the token(s) on behalf of their owner. | ||
* @dev MUST be equivalent to calling `setExplicitApprovalFor(operator, tokenId, approved)` for each `tokenId` in | ||
* the array. | ||
*/ | ||
function setExplicitApproval(address operator, uint256[] memory tokenIds, bool approved) external; | ||
|
||
/** | ||
* @notice Revokes all explicit approvals granted by `msg.sender`. | ||
* @dev MUST emit `AllExplicitApprovalsRevoked(msg.sender)`. | ||
*/ | ||
function revokeAllExplicitApprovals() external; | ||
|
||
/** | ||
* @notice Revokes all excplicit approvals granted for the specified token. | ||
* @dev Throws if `msg.sender` is not the current NFT owner, or an authorised operator of the current owner. | ||
* @dev MUST emit `AllExplicitApprovalsRevoked(msg.sender, tokenId)`. | ||
*/ | ||
function revokeAllExplicitApprovals(uint256 tokenId) external; | ||
|
||
/** | ||
* @notice Query whether an address is an approved operator for a token. | ||
*/ | ||
function isExplicitlyApprovedFor(address operator, uint256 tokenId) external view returns (bool); | ||
} | ||
|
||
interface IERC6464AnyApproval is ERC721 { | ||
/** | ||
* @notice Returns true if any of the following criteria are met: | ||
* 1. `isExplicitlyApprovedFor(operator, tokenId) == true`; OR | ||
* 2. `isApprovedForAll(ownerOf(tokenId), operator) == true`; OR | ||
* 3. `getApproved(tokenId) == operator`. | ||
* @dev The criteria MUST be extended if other mechanism(s) for approving operators are introduced. The criteria | ||
* MUST include all approval approaches. | ||
*/ | ||
function isApprovedFor(address operator, uint256 tokenId) external view returns (bool); | ||
} | ||
|
||
interface IERC6464Events { | ||
/** | ||
* @notice Emitted when approval is explicitly granted or revoked for a token. | ||
*/ | ||
event ExplicitApprovalFor(address indexed operator, uint256 indexed tokenId, bool approved); | ||
|
||
/** | ||
* @notice Emitted when all explicit approvals, as granted by either `setExplicitApprovalFor()` function, are | ||
* revoked for all tokens. | ||
* @dev MUST be emitted upon calls to `revokeAllExplicitApprovals()`. | ||
*/ | ||
event AllExplicitApprovalsRevoked(address indexed owner); | ||
|
||
/** | ||
* @notice Emitted when all explicit approvals, as granted by either `setExplicitApprovalFor()` function, are | ||
* revoked for the specific token. | ||
* @param owner MUST be `ownerOf(tokenId)` as per ERC721; in the case of revocation due to transfer, this MUST be | ||
* the `from` address expected to be emitted in the respective `ERC721.Transfer()` event. | ||
*/ | ||
event AllExplicitApprovalsRevoked(address indexed owner, uint256 indexed tokenId); | ||
} |
Oops, something went wrong.