forked from ethereum/EIPs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update EIP-7015: allow arbitratry
TYPEHASH
and correponding fields …
…for `structHash` Merged by EIP-Bot.
- Loading branch information
Showing
6 changed files
with
803 additions
and
75 deletions.
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
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,70 @@ | ||
// SPDX-License-Identifier: CC0-1.0 | ||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; | ||
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | ||
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; | ||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
|
||
import "./EIP7015.sol"; | ||
|
||
contract DelegatedErc721 is ERC7015, ERC721, ERC721URIStorage, Ownable { | ||
error AlreadyMinted(); | ||
error NotAuthorized(); | ||
|
||
uint256 private _nextTokenId; | ||
|
||
bytes32 public constant TYPEHASH = | ||
keccak256("CreatorAttribution(string uri,uint256 nonce)"); | ||
|
||
// mapping of signature nonce to if it has been minted | ||
mapping(uint256 => bool) public minted; | ||
|
||
constructor( | ||
address initialOwner | ||
) EIP712("ERC7015", "1") ERC721("My Token", "TKN") Ownable(initialOwner) {} | ||
|
||
function delegatedSafeMint( | ||
address to, | ||
string memory uri, | ||
uint256 nonce, | ||
address creator, | ||
bytes calldata signature | ||
) external { | ||
uint256 tokenId = _nextTokenId++; | ||
|
||
if (!isAuthorizedToCreate(creator)) revert NotAuthorized(); | ||
|
||
// validate that the nonce has not been used | ||
if (minted[nonce]) revert AlreadyMinted(); | ||
minted[nonce] = true; | ||
|
||
bytes32 structHash = keccak256( | ||
abi.encode(TYPEHASH, keccak256(bytes(uri)), nonce) | ||
); | ||
|
||
_validateSignature(structHash, creator, signature); | ||
|
||
_safeMint(to, tokenId); | ||
_setTokenURI(tokenId, uri); | ||
} | ||
|
||
// override required function to define if a signer is authorized to create | ||
function isAuthorizedToCreate(address signer) internal view returns (bool) { | ||
return signer == owner(); | ||
} | ||
|
||
// The following functions are overrides required by Solidity. | ||
function tokenURI( | ||
uint256 tokenId | ||
) public view override(ERC721, ERC721URIStorage) returns (string memory) { | ||
return super.tokenURI(tokenId); | ||
} | ||
|
||
function supportsInterface( | ||
bytes4 interfaceId | ||
) public view override(ERC721, ERC721URIStorage) returns (bool) { | ||
return super.supportsInterface(interfaceId); | ||
} | ||
} |
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,56 @@ | ||
// SPDX-License-Identifier: CC0-1.0 | ||
pragma solidity 0.8.20; | ||
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; | ||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; | ||
import "@openzeppelin/contracts/interfaces/IERC1271.sol"; | ||
|
||
abstract contract ERC7015 is EIP712 { | ||
error Invalid_Signature(); | ||
event CreatorAttribution( | ||
bytes32 structHash, | ||
string domainName, | ||
string version, | ||
address creator, | ||
bytes signature | ||
); | ||
|
||
/// @notice Define magic value to verify smart contract signatures (ERC1271). | ||
bytes4 internal constant MAGIC_VALUE = | ||
bytes4(keccak256("isValidSignature(bytes32,bytes)")); | ||
|
||
function _validateSignature( | ||
bytes32 structHash, | ||
address creator, | ||
bytes memory signature | ||
) internal { | ||
if (!_isValid(structHash, creator, signature)) revert Invalid_Signature(); | ||
emit CreatorAttribution(structHash, "ERC7015", "1", creator, signature); | ||
} | ||
|
||
function _isValid( | ||
bytes32 structHash, | ||
address signer, | ||
bytes memory signature | ||
) internal view returns (bool) { | ||
require(signer != address(0), "cannot validate"); | ||
|
||
bytes32 digest = _hashTypedDataV4(structHash); | ||
|
||
// if smart contract is the signer, verify using ERC-1271 smart-contract | ||
/// signature verification method | ||
if (signer.code.length != 0) { | ||
try IERC1271(signer).isValidSignature(digest, signature) returns ( | ||
bytes4 magicValue | ||
) { | ||
return MAGIC_VALUE == magicValue; | ||
} catch { | ||
return false; | ||
} | ||
} | ||
|
||
// otherwise, recover signer and validate that it matches the expected | ||
// signer | ||
address recoveredSigner = ECDSA.recover(digest, signature); | ||
return recoveredSigner == signer; | ||
} | ||
} |
Oops, something went wrong.