Skip to content

Commit

Permalink
Updated contract support public token and erc721 and erc1155 interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
tienngovan committed Mar 1, 2024
1 parent 6bffb9f commit 0d23a3f
Show file tree
Hide file tree
Showing 2 changed files with 254 additions and 89 deletions.
127 changes: 82 additions & 45 deletions contracts/OwnerData.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";

/**
* @title OwnerData
* @dev Manages addition data for ERC721 or ERC1155 token.
*/
contract OwnerData is Context, Ownable {
string private constant SIGNED_MESSAGE = "Authorize to write your data to the contract";
address private immutable _trustee;

contract IFF {
function ownerOf(uint256 tokenId) public view returns (address) {}
}

string constant SIGNED_MESSAGE = "Authorize to write your data to the contract";

contract OwnerData is Context {
struct Data {
address owner;
bytes dataHash;
string metadata;
}

struct Signature {
bytes ownerSign;
uint256 expiryBlock;
Expand All @@ -25,78 +31,109 @@ contract OwnerData is Context {
uint8 v;
}

address private _trustee;
struct SignedAddParams {
address contractAddress;
uint256 tokenID;
Data data;
Signature signature;
}

// contractAddress => tokenID => Data[]
mapping(address => mapping(uint256 => Data[])) private _tokenData;
// contractAddress => tokenID => owner => bool
mapping(address => mapping(uint256 => mapping(address => bool))) private _tokenDataOwner;
// contractAddress => tokenID => bool
mapping(address => mapping(uint256 => bool)) private _publicTokens;

event DataAdded(address indexed contractAddress, uint256 indexed tokenID, Data data);

constructor(address trustee_) {
require(trustee_ != address(0), "OwnerData: Trustee is the zero address");
_trustee = trustee_;
}

function add(address contractAddress, uint256 tokenID, Data calldata data) external {
_addData(_msgSender(), contractAddress, tokenID, data);
function add(address contractAddress_, uint256 tokenID_, Data calldata data_) external payable {
require(!_publicTokens[contractAddress_][tokenID_] || msg.value > 0, "OwnerData: Payment required for public token");
_addData(_msgSender(), contractAddress_, tokenID_, data_);
if (msg.value > 0) {
payable(owner()).transfer(msg.value);
}
}

function get(address contractAddress_, uint256 tokenID_) external view returns (Data[] memory) {
return _tokenData[contractAddress_][tokenID_];
}

function setPublicTokens(address[] memory contractAddresses_, uint256[] memory tokenIDs_, bool isPublic_) external onlyOwner {
require(contractAddresses_.length == tokenIDs_.length, "OwnerData: Arrays length mismatch");
for (uint256 i = 0; i < contractAddresses_.length; i++) {
_publicTokens[contractAddresses_[i]][tokenIDs_[i]] = isPublic_;
}
}

function signedAdd(
address contractAddress,
uint256 tokenID,
Data calldata data,
Signature calldata signature
) external {
_validateSignature(signature);
address account = _recoverOwnerSignature(signature.ownerSign);
_addData(account, contractAddress, tokenID, data);
function signedAdd(SignedAddParams[] calldata params_) external {
for (uint256 i = 0; i < params_.length; i++) {
_signedAdd(params_[i]);
}
}


function get(address contractAddress, uint256 tokenID) external view returns (Data[] memory) {
return _tokenData[contractAddress][tokenID];
function _signedAdd(SignedAddParams calldata params_) private {
_validateSignature(params_.signature);
if (_publicTokens[params_.contractAddress][params_.tokenID]) {
_addData(params_.data.owner, params_.contractAddress, params_.tokenID, params_.data);
} else {
address account = _recoverOwnerSignature(params_.signature.ownerSign);
_addData(account, params_.contractAddress, params_.tokenID, params_.data);
}
}

function _addData(
address sender,
address contractAddress,
uint256 tokenID,
Data calldata data
address sender_,
address contractAddress_,
uint256 tokenID_,
Data calldata data_
) private {
require(_isOwner(contractAddress, tokenID, sender), "OwnerData: sender is not the owner");
require(data.owner == sender, "OwnerData: data owner mismatch");
require(data.dataHash.length > 0, "OwnerData: dataHash is empty");
require(!_tokenDataOwner[contractAddress][tokenID][data.owner], "OwnerData: data already added");
require(data_.owner == sender_, "OwnerData: data owner and sender mismatch");
require(data_.dataHash.length > 0, "OwnerData: dataHash is empty");

_tokenData[contractAddress][tokenID].push(data);
_tokenDataOwner[contractAddress][tokenID][data.owner] = true;
if (!_publicTokens[contractAddress_][tokenID_]) {
require(_isOwner(contractAddress_, tokenID_, data_.owner), "OwnerData: sender is not the owner");
require(!_tokenDataOwner[contractAddress_][tokenID_][data_.owner], "OwnerData: data already added");
_tokenDataOwner[contractAddress_][tokenID_][data_.owner] = true;
}

emit DataAdded(contractAddress, tokenID, data);
}
_tokenData[contractAddress_][tokenID_].push(data_);

function _validateSignature(Signature calldata signature) private view {
require(signature.expiryBlock >= block.number, "OwnerData: signature expired");
emit DataAdded(contractAddress_, tokenID_, data_);
}

function _validateSignature(Signature calldata signature_) private view {
require(block.number < signature_.expiryBlock, "OwnerData: signature expired");
bytes32 message = keccak256(
abi.encode(block.chainid, address(this), signature.ownerSign, signature.expiryBlock)
abi.encode(block.chainid, address(this), signature_.ownerSign, signature_.expiryBlock)
);
address reqSigner = ECDSA.recover(
ECDSA.toEthSignedMessageHash(message),
signature.v,
signature.r,
signature.s
signature_.v,
signature_.r,
signature_.s
);
require(reqSigner == _trustee, "OwnerData: invalid signature");
require(reqSigner == _trustee, "OwnerData: Invalid signature");
}

function _recoverOwnerSignature(bytes memory signature) private view returns (address) {
function _recoverOwnerSignature(bytes memory signature_) private view returns (address) {
bytes memory message = abi.encodePacked(SIGNED_MESSAGE, " ", Strings.toHexString(address(this)), ".");
return ECDSA.recover(ECDSA.toEthSignedMessageHash(message), signature);
return ECDSA.recover(ECDSA.toEthSignedMessageHash(message), signature_);
}

function _isOwner(address contractAddress, uint256 tokenID, address account) private view returns (bool) {
return IFF(contractAddress).ownerOf(tokenID) == account;
if (IERC165(contractAddress).supportsInterface(type(IERC1155).interfaceId)) {
return IERC1155(contractAddress).balanceOf(account, tokenID) > 0;
}
if (IERC165(contractAddress).supportsInterface(type(IERC721).interfaceId)) {
return IERC721(contractAddress).ownerOf(tokenID) == account;
}
return false;
}

event DataAdded(address indexed contractAddress, uint256 indexed tokenID, Data data);
}
Loading

0 comments on commit 0d23a3f

Please sign in to comment.