Skip to content

Commit

Permalink
Add TPLERC721Permissioned instance
Browse files Browse the repository at this point in the history
  • Loading branch information
0age committed Oct 28, 2018
1 parent 0498dd7 commit d76a8b4
Show file tree
Hide file tree
Showing 6 changed files with 2,168 additions and 0 deletions.
1 change: 1 addition & 0 deletions .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"rules": {
"max-line-length": 131,
"no-inline-assembly": false,
"not-rely-on-time": false,
"avoid-tx-origin": false,
"multiple-sends": false,
"function-max-lines": false,
Expand Down
258 changes: 258 additions & 0 deletions contracts/examples/token/ERC721/TPLERC721Permissioned.sol
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));
}
}
53 changes: 53 additions & 0 deletions contracts/mock/TPLERC721PermissionedInstance.sol
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);
}
}
Loading

0 comments on commit d76a8b4

Please sign in to comment.