diff --git a/contracts/OwnerData.sol b/contracts/OwnerData.sol index 79b5904..a44d582 100644 --- a/contracts/OwnerData.sol +++ b/contracts/OwnerData.sol @@ -15,7 +15,9 @@ import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; */ contract OwnerData is Context, Ownable { string private constant SIGNED_MESSAGE = "Authorize to write your data to the contract"; - address private immutable _trustee; + address private immutable _signer; + address private immutable _costReceiver; + uint256 private immutable _cost; struct Data { address owner; @@ -47,21 +49,45 @@ contract OwnerData is Context, Ownable { 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_; + constructor(address signer_, address costReceiver_, uint256 cost_) { + require(signer_ != address(0), "OwnerData: Trustee is the zero address"); + require(costReceiver_ != address(0), "OwnerData: Cost receiver is the zero address"); + require(cost_ > 0, "OwnerData: Cost is zero"); + _signer = signer_; + _costReceiver = costReceiver_; + _cost = cost_; } function add(address contractAddress_, uint256 tokenID_, Data calldata data_) external payable { - require(!_publicTokens[contractAddress_][tokenID_] || msg.value > 0, "OwnerData: Payment required for public token"); + require(!_publicTokens[contractAddress_][tokenID_] || msg.value == _cost, "OwnerData: Payment required for public token"); _addData(_msgSender(), contractAddress_, tokenID_, data_); if (msg.value > 0) { - payable(owner()).transfer(msg.value); + payable(_costReceiver).transfer(msg.value); } } - function get(address contractAddress_, uint256 tokenID_) external view returns (Data[] memory) { - return _tokenData[contractAddress_][tokenID_]; + function get(address contractAddress_, uint256 tokenID_, uint256 startIndex, uint256 count) public view returns (Data[] memory) { + Data[] memory data = _tokenData[contractAddress_][tokenID_]; + require(startIndex >= 0 && count > 0 && startIndex < data.length, "OwnerData: Invalid parameters"); + if (count > data.length - startIndex) { + count = data.length - startIndex; + } + Data[] memory result = new Data[](count); + for (uint256 i = 0; i < count; i++) { + result[i] = data[startIndex + i]; + } + return result; + } + + function remove(address contractAddress_, uint256 tokenID_, uint256[] calldata indexes_) external { + Data[] storage data = _tokenData[contractAddress_][tokenID_]; + for (uint256 i = 0; i < indexes_.length; i++) { + require(indexes_[i] < data.length, "Index out of bounds"); + if (indexes_[i] != data.length - 1) { + data[indexes_[i]] = data[data.length - 1]; + } + data.pop(); + } } function setPublicTokens(address[] memory contractAddresses_, uint256[] memory tokenIDs_, bool isPublic_) external onlyOwner { @@ -119,7 +145,7 @@ contract OwnerData is Context, Ownable { signature_.r, signature_.s ); - require(reqSigner == _trustee, "OwnerData: Invalid signature"); + require(reqSigner == _signer, "OwnerData: Invalid signature"); } function _recoverOwnerSignature(bytes memory signature_) private view returns (address) { diff --git a/migrations/252_owner_data.js b/migrations/252_owner_data.js index f618480..e2ff33b 100644 --- a/migrations/252_owner_data.js +++ b/migrations/252_owner_data.js @@ -6,5 +6,8 @@ const argv = require("minimist")(process.argv.slice(2), { module.exports = function (deployer) { const trustee = argv.trustee || "0xdB33365a8730de2F7574ff1189fB9D337bF4c36d"; - deployer.deploy(OwnerData, trustee); + const costReceiver = + argv.costReceiver || "0xdB33365a8730de2F7574ff1189fB9D337bF4c36d"; + const cost = argv.cost || "1000000000000000000"; + deployer.deploy(OwnerData, trustee, costReceiver, cost); }; diff --git a/test/owner_data.js b/test/owner_data.js index 1273b88..81b9118 100644 --- a/test/owner_data.js +++ b/test/owner_data.js @@ -19,11 +19,15 @@ contract("OwnerData", async (accounts) => { before(async function () { this.signer = accounts[0]; this.trustee = accounts[1]; - this.signTrustee = accounts[5]; + this.ownerDataSigner = accounts[5]; this.vault = await FeralfileVault.new(this.signer); - this.ownerDataContract = await OwnerData.new(this.signTrustee); - this.seriesIds = [1, 2]; - this.seriesMaxSupply = [100, 1]; + this.ownerDataContract = await OwnerData.new( + this.ownerDataSigner, + COST_RECEIVER, + web3.utils.toWei("0.015", "ether"), + ); + this.seriesIds = [1, 2, 3]; + this.seriesMaxSupply = [100, 1, 1]; this.seriesArtworkMaxSupply = [1, 100]; this.exhibitionContract = await FeralfileExhibitionV4.new( "Feral File V4 Test", @@ -51,12 +55,13 @@ contract("OwnerData", async (accounts) => { "0x23221e5403511CeC833294D2B1B006e9D639A61b", ], [this.seriesIds[1], 200001, accounts[0]], + [this.seriesIds[2], 200002, accounts[0]], ]); await this.exhibitionContract.addTrustee(this.trustee); await this.ownerDataContract.setPublicTokens( - [this.exhibitionContract.address], - [200001], + [this.exhibitionContract.address, this.exhibitionContract.address], + [200001, 200002], true, ); @@ -90,6 +95,8 @@ contract("OwnerData", async (accounts) => { const data = await this.ownerDataContract.get( this.exhibitionContract.address, 100001, + 0, + 100, ); assert.equal(data[0].owner, accounts[0]); assert.equal( @@ -179,6 +186,8 @@ contract("OwnerData", async (accounts) => { const data = await this.ownerDataContract.get( this.exhibitionContract.address, 100002, + 0, + 100, ); assert.equal(data.length, 3); @@ -229,6 +238,62 @@ contract("OwnerData", async (accounts) => { } }); + it("test removing data successfully", async function () { + const cid = "QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc"; + const cidBytes = web3.utils.fromAscii(cid); + await this.ownerDataContract.add( + this.exhibitionContract.address, + 200002, + [accounts[0], cidBytes, "{duration: 1000}"], + { from: accounts[0], value: web3.utils.toWei("0.015", "ether") }, + ); + await this.ownerDataContract.add( + this.exhibitionContract.address, + 200002, + [accounts[1], cidBytes, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + await this.ownerDataContract.add( + this.exhibitionContract.address, + 200002, + [accounts[2], cidBytes, "{duration: 1000}"], + { from: accounts[2], value: web3.utils.toWei("0.015", "ether") }, + ); + await this.ownerDataContract.add( + this.exhibitionContract.address, + 200002, + [accounts[1], cidBytes, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + await this.ownerDataContract.add( + this.exhibitionContract.address, + 200002, + [accounts[0], cidBytes, "{duration: 1000}"], + { from: accounts[0], value: web3.utils.toWei("0.015", "ether") }, + ); + const res = await this.ownerDataContract.get( + this.exhibitionContract.address, + 200002, + 0, + 100, + ); + assert.equal(res.length, 5); + const tx = await this.ownerDataContract.remove( + this.exhibitionContract.address, + 200002, + [4, 2], + { from: accounts[0] }, + ); + + const res2 = await this.ownerDataContract.get( + this.exhibitionContract.address, + 200002, + 0, + 100, + ); + assert.equal(res2.length, 3); + }); + it("test adding with signed add function", async function () { const cid = "QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc"; const cidBytes = web3.utils.fromAscii(cid); @@ -286,7 +351,7 @@ contract("OwnerData", async (accounts) => { this.exhibitionContract.address, 200001, [accounts[0], cidBytes, "{duration: 1000}"], - { from: accounts[0], value: web3.utils.toWei("0.001", "ether") }, + { from: accounts[0], value: web3.utils.toWei("0.015", "ether") }, ); assert.equal(tx1.logs[0].event, "DataAdded"); @@ -294,7 +359,7 @@ contract("OwnerData", async (accounts) => { this.exhibitionContract.address, 200001, [accounts[2], cidBytes, "{duration: 1000}"], - { from: accounts[2], value: web3.utils.toWei("0.02", "ether") }, + { from: accounts[2], value: web3.utils.toWei("0.015", "ether") }, ); assert.equal(tx2.logs[0].event, "DataAdded"); });