-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
2,168 additions
and
0 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
258 changes: 258 additions & 0 deletions
258
contracts/examples/token/ERC721/TPLERC721Permissioned.sol
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,258 @@ | ||
pragma solidity ^0.4.25; | ||
|
||
import "openzeppelin-solidity/contracts/token/ERC721/ERC721.sol"; | ||
import "./TPLERC721PermissionedInterface.sol"; | ||
import "../../../AttributeRegistryInterface.sol"; | ||
|
||
|
||
/** | ||
* @title Permissioned ERC721 token: ownership is restricted to valid accounts. | ||
*/ | ||
contract TPLERC721Permissioned is ERC721, TPLERC721PermissionedInterface { | ||
|
||
// Declare registry interface, used to request attributes from a jurisdiction. | ||
AttributeRegistryInterface private _registry; | ||
|
||
// Declare attribute ID required in order to hold tokens, | ||
uint256 private _validAttributeTypeID; | ||
|
||
/** | ||
* @notice The constructor function, with an associated attribute registry at | ||
* `registry` and an assignable attribute type with ID `validAttributeTypeID`. | ||
* @param registry address The account of the associated attribute registry. | ||
* @param validAttributeTypeID uint256 The ID of the required attribute type. | ||
* @dev Note that it may be appropriate to require that the referenced | ||
* attribute registry supports the correct interface via EIP-165. | ||
*/ | ||
constructor( | ||
AttributeRegistryInterface registry, | ||
uint256 validAttributeTypeID | ||
) public { | ||
_registry = AttributeRegistryInterface(registry); | ||
_validAttributeTypeID = validAttributeTypeID; | ||
} | ||
|
||
/** | ||
* @notice Check if a transfer of the NFT with ID `tokenId` on behalf of | ||
* account `from` to a recipient at account `to` with `msg.value` of `value` | ||
* and data `data` is approved. The check must fail if the recipient of the | ||
* transfer does not correctly implement `onERC721Received`. | ||
* @param from address The current owner of the NFT. | ||
* @param to address The new owner. | ||
* @param tokenId uint256 The NFT to transfer. | ||
* @param value uint256 The amount of ether to include with the transaction. | ||
* @param data bytes Additional data with no specified format to be included. | ||
* @return Bool indicating if transfer is approved & byte with a status code. | ||
*/ | ||
function canSafeTransferFrom( | ||
address from, | ||
address to, | ||
uint256 tokenId, | ||
uint256 value, | ||
bytes data | ||
) external view returns (bool, bytes1) { | ||
// This implementation does not allow attaching a value to the transfer. | ||
if (value > 0) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The spender must be approved or the owner of the NFT. | ||
if (!_isApprovedOrOwner(msg.sender, tokenId)) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The to address cannot be the null address. | ||
if (to == address(0)) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The receiver must return the required magic number (if it is a contract). | ||
if (!_checkOnERC721Received(from, to, tokenId, data)) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The recipient of the transfer must have the correct attribute assigned. | ||
if (!_registry.hasAttribute(to, _validAttributeTypeID)) { | ||
return (false, bytes1(hex"10")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The transfer is approved, return true and the success status code. | ||
return (true, bytes1(hex"01")); | ||
} | ||
|
||
/** | ||
* @notice Check if a transfer of the NFT with ID `tokenId` on behalf of | ||
* account `from` to a recipient at account `to` with `msg.value` of `value` | ||
* is approved. The check must fail if the recipient of the transfer does not | ||
* correctly implement `onERC721Received`. | ||
* @param from address The current owner of the NFT. | ||
* @param to address The new owner. | ||
* @param tokenId uint256 The NFT to transfer. | ||
* @param value uint256 The amount of ether to include with the transaction. | ||
* @return Bool indicating if transfer is approved & byte with a status code. | ||
*/ | ||
function canSafeTransferFrom( | ||
address from, | ||
address to, | ||
uint256 tokenId, | ||
uint256 value | ||
) external view returns (bool, bytes1) { | ||
// This implementation does not allow attaching a value to the transfer. | ||
if (value > 0) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The spender must be approved or the owner of the NFT. | ||
if (!_isApprovedOrOwner(msg.sender, tokenId)) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The to address cannot be the null address. | ||
if (to == address(0)) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The receiver must return the required magic number (if it is a contract). | ||
if (!_checkOnERC721Received(from, to, tokenId, "")) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The recipient of the transfer must have the correct attribute assigned. | ||
if (!_registry.hasAttribute(to, _validAttributeTypeID)) { | ||
return (false, bytes1(hex"10")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The transfer is approved, return true and the success status code. | ||
return (true, bytes1(hex"01")); | ||
} | ||
|
||
/** | ||
* @notice Check if a transfer of the NFT with ID `tokenId` on behalf of | ||
* account `from` to a recipient at account `to` with `msg.value` of `value` | ||
* is approved. THE CALLER IS RESPONSIBLE TO CONFIRM THAT `to` IS CAPABLE OF | ||
* RECEIVING NFTS OR ELSE THEY MAY BE PERMANENTLY LOST. | ||
* @param from address The current owner of the NFT. | ||
* @param to address The new owner. | ||
* @param tokenId uint256 The NFT to transfer. | ||
* @param value uint256 The amount of ether to include with the transaction. | ||
* @return Bool indicating if transfer is approved & byte with a status code. | ||
*/ | ||
function canTransferFrom( | ||
address from, | ||
address to, | ||
uint256 tokenId, | ||
uint256 value | ||
) external view returns (bool, bytes1) { | ||
// avoid an unused variable warning. | ||
from; | ||
|
||
// This implementation does not allow attaching a value to the transfer. | ||
if (value > 0) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The spender must be approved or the owner of the NFT. | ||
if (!_isApprovedOrOwner(msg.sender, tokenId)) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The to address cannot be the null address. | ||
if (to == address(0)) { | ||
return (false, bytes1(hex"A0")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The recipient of the transfer must have the correct attribute assigned. | ||
if (!_registry.hasAttribute(to, _validAttributeTypeID)) { | ||
return (false, bytes1(hex"10")); // NOTE: error codes are not standardized | ||
} | ||
|
||
// The transfer is approved, return true and the success status code. | ||
return (true, bytes1(hex"01")); | ||
} | ||
|
||
/** | ||
* @notice Get the account of the utilized attribute registry. | ||
* @return The account of the registry. | ||
*/ | ||
function getRegistry() external view returns (address) { | ||
return address(_registry); | ||
} | ||
|
||
/** | ||
* @notice Get the ID of the attribute type required to receive tokens. | ||
* @return The ID of the required attribute type. | ||
*/ | ||
function getValidAttributeID() external view returns (uint256) { | ||
return _validAttributeTypeID; | ||
} | ||
|
||
/** | ||
* @dev Transfers the ownership of a given token ID to another address | ||
* Usage of this method is discouraged, use `safeTransferFrom` when possible | ||
* Requires the msg sender to be the owner, approved, or operator | ||
* @param from current owner of the token | ||
* @param to address to receive the ownership of the given token ID | ||
* @param tokenId uint256 ID of the token to be transferred | ||
*/ | ||
function transferFrom( | ||
address from, | ||
address to, | ||
uint256 tokenId | ||
) | ||
public | ||
{ | ||
require( | ||
_registry.hasAttribute(to, _validAttributeTypeID), | ||
"Transfer failed - receiver is not approved." | ||
); | ||
super.transferFrom(from, to, tokenId); | ||
} | ||
|
||
/** | ||
* @dev Safely transfers the ownership of a given token ID to another address | ||
* If the target address is a contract, it must implement `onERC721Received`, | ||
* which is called upon a safe transfer, and return the magic value | ||
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; | ||
* otherwise, the transfer is reverted. | ||
* | ||
* Requires the msg sender to be the owner, approved, or operator | ||
* @param from current owner of the token | ||
* @param to address to receive the ownership of the given token ID | ||
* @param tokenId uint256 ID of the token to be transferred | ||
*/ | ||
function safeTransferFrom( | ||
address from, | ||
address to, | ||
uint256 tokenId | ||
) | ||
public | ||
{ | ||
// solium-disable-next-line arg-overflow | ||
safeTransferFrom(from, to, tokenId, hex""); | ||
} | ||
|
||
/** | ||
* @dev Safely transfers the ownership of a given token ID to another address | ||
* If the target address is a contract, it must implement `onERC721Received`, | ||
* which is called upon a safe transfer, and return the magic value | ||
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; | ||
* otherwise, the transfer is reverted. | ||
* Requires the msg sender to be the owner, approved, or operator | ||
* @param from current owner of the token | ||
* @param to address to receive the ownership of the given token ID | ||
* @param tokenId uint256 ID of the token to be transferred | ||
* @param data bytes data to send along with a safe transfer check | ||
*/ | ||
function safeTransferFrom( | ||
address from, | ||
address to, | ||
uint256 tokenId, | ||
bytes data | ||
) | ||
public | ||
{ | ||
transferFrom(from, to, tokenId); | ||
// solium-disable-next-line arg-overflow | ||
require(_checkOnERC721Received(from, to, tokenId, data)); | ||
} | ||
} |
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,53 @@ | ||
pragma solidity ^0.4.25; | ||
|
||
import "openzeppelin-solidity/contracts/token/ERC721/ERC721Enumerable.sol"; | ||
import "../examples/token/ERC721/TPLERC721Permissioned.sol"; | ||
import "../AttributeRegistryInterface.sol"; | ||
|
||
|
||
/** | ||
* @title An instance of TPLERC721Permissioned with an initial token. | ||
*/ | ||
contract TPLERC721PermissionedInstance is TPLERC721Permissioned, ERC721Enumerable { | ||
/** | ||
* @notice The constructor function, with an associated attribute registry at | ||
* `registry`, an assignable attribute type with ID `validAttributeTypeID`, and | ||
* an initial NFT assigned to the creator. | ||
* @param registry address The account of the associated attribute registry. | ||
* @param validAttributeTypeID uint256 The ID of the required attribute type. | ||
* @dev Note that it may be appropriate to require that the referenced | ||
* attribute registry supports the correct interface via EIP-165. | ||
*/ | ||
constructor( | ||
AttributeRegistryInterface registry, | ||
uint256 validAttributeTypeID | ||
) public TPLERC721Permissioned(registry, validAttributeTypeID) { | ||
_mockMint(); | ||
} | ||
|
||
function _mockMint() internal { | ||
bytes32 initialTokenInput = keccak256( | ||
abi.encodePacked( | ||
msg.sender, | ||
blockhash(block.number - 1), | ||
now | ||
) | ||
); | ||
|
||
/* | ||
bytes32 out; | ||
for (uint i = 0; i < 32; i++) { | ||
uint8 candidate = uint8(initialTokenInput[i]); | ||
while (candidate > 153 || candidate % 16 > 9) { | ||
candidate = candidate * 127; | ||
} | ||
out |= bytes32(bytes1(candidate) & 0xFF) >> (i * 8); | ||
} | ||
uint256 tokenId = uint256(out); | ||
*/ | ||
uint256 tokenId = uint256(initialTokenInput); | ||
|
||
_mint(msg.sender, tokenId); | ||
} | ||
} |
Oops, something went wrong.