diff --git a/contracts/OwnerData.sol b/contracts/OwnerData.sol index 05d2241..5fda47f 100644 --- a/contracts/OwnerData.sol +++ b/contracts/OwnerData.sol @@ -71,7 +71,7 @@ contract OwnerData is Context, Ownable { } function add(address contractAddress_, uint256 tokenID_, Data calldata data_) external payable { - if (_publicTokens[contractAddress_][tokenID_] && msg.value != cost) { + if (_publicTokens[contractAddress_][tokenID_] && msg.value < cost) { revert PaymentRequiredForPublicToken(); } _addData(_msgSender(), contractAddress_, tokenID_, data_); @@ -88,6 +88,7 @@ contract OwnerData is Context, Ownable { if (startIndex + count > data.length) { count = data.length - startIndex; } + _sort(data); Data[] memory result = new Data[](count); for (uint256 i = 0; i < count; i++) { result[i] = data[startIndex + i]; @@ -95,6 +96,23 @@ contract OwnerData is Context, Ownable { return result; } + function getByOwner(address contractAddress_, uint256 tokenID_, address owner_) public view returns (Data[] memory) { + Data[] memory data = _tokenData[contractAddress_][tokenID_]; + Data[] memory temp = new Data[](data.length); + uint256 count = 0; + for (uint256 i = 0; i < data.length; i++) { + if (data[i].owner == owner_) { + temp[count] = data[i]; + count++; + } + } + Data[] memory result = new Data[](count); + for (uint256 i = 0; i < count; i++) { + result[i] = temp[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++) { @@ -152,12 +170,25 @@ contract OwnerData is Context, Ownable { } data_.blockNumber = block.number; - + _tokenData[contractAddress_][tokenID_].push(data_); emit DataAdded(contractAddress_, tokenID_, data_); } + function _sort(Data[] memory data_) private pure { + bool swapped; + do { + swapped = false; + for (uint256 i = 0; i < data_.length - 1; i++) { + if (data_[i].blockNumber > data_[i + 1].blockNumber) { + (data_[i], data_[i + 1]) = (data_[i + 1], data_[i]); + swapped = true; + } + } + } while (swapped); + } + function _validateSignature(Signature calldata signature_) private view { if (block.number > signature_.expiryBlock) { revert InvalidSignature(); diff --git a/test/owner_data.js b/test/owner_data.js index be68a89..679a306 100644 --- a/test/owner_data.js +++ b/test/owner_data.js @@ -26,9 +26,8 @@ contract("OwnerData", async (accounts) => { COST_RECEIVER, web3.utils.toWei("0.015", "ether"), ); - this.seriesIds = [1, 2, 3]; - this.seriesMaxSupply = [100, 1, 1]; - this.seriesArtworkMaxSupply = [1, 100]; + this.seriesIds = [1, 2, 3, 4, 5]; + this.seriesMaxSupply = [100, 1, 1, 1, 1]; this.exhibitionContract = await FeralfileExhibitionV4.new( "Feral File V4 Test", "FFv4", @@ -55,13 +54,20 @@ contract("OwnerData", async (accounts) => { "0x23221e5403511CeC833294D2B1B006e9D639A61b", ], [this.seriesIds[1], 200001, accounts[0]], - [this.seriesIds[2], 200002, accounts[0]], + [this.seriesIds[2], 300001, accounts[0]], + [this.seriesIds[3], 400001, accounts[0]], + [this.seriesIds[4], 500001, accounts[0]], ]); await this.exhibitionContract.addTrustee(this.trustee); await this.ownerDataContract.setPublicTokens( - [this.exhibitionContract.address, this.exhibitionContract.address], - [200001, 200002], + [ + this.exhibitionContract.address, + this.exhibitionContract.address, + this.exhibitionContract.address, + this.exhibitionContract.address, + ], + [200001, 300001, 400001, 500001], true, ); @@ -240,51 +246,51 @@ contract("OwnerData", async (accounts) => { const cidBytes = web3.utils.fromAscii(cid); await this.ownerDataContract.add( this.exhibitionContract.address, - 200002, + 300001, [accounts[0], cidBytes, 0, "{duration: 1000}"], { from: accounts[0], value: web3.utils.toWei("0.015", "ether") }, ); await this.ownerDataContract.add( this.exhibitionContract.address, - 200002, + 300001, [accounts[1], cidBytes, 0, "{duration: 1000}"], { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, ); await this.ownerDataContract.add( this.exhibitionContract.address, - 200002, + 300001, [accounts[2], cidBytes, 0, "{duration: 1000}"], { from: accounts[2], value: web3.utils.toWei("0.015", "ether") }, ); await this.ownerDataContract.add( this.exhibitionContract.address, - 200002, + 300001, [accounts[1], cidBytes, 0, "{duration: 1000}"], { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, ); await this.ownerDataContract.add( this.exhibitionContract.address, - 200002, + 300001, [accounts[0], cidBytes, 0, "{duration: 1000}"], { from: accounts[0], value: web3.utils.toWei("0.015", "ether") }, ); const res = await this.ownerDataContract.get( this.exhibitionContract.address, - 200002, + 300001, 0, 100, ); assert.equal(res.length, 5); const tx = await this.ownerDataContract.remove( this.exhibitionContract.address, - 200002, + 300001, [4, 2], { from: accounts[0] }, ); const res2 = await this.ownerDataContract.get( this.exhibitionContract.address, - 200002, + 300001, 0, 100, ); @@ -438,4 +444,111 @@ contract("OwnerData", async (accounts) => { assert.equal(error.reason, "Custom error (could not decode)"); } }); + + it("test sort data by timestamp", async function () { + const cid1 = "123"; + const cid2 = "456"; + const cid3 = "789"; + const cid4 = "555"; + + const cidBytes1 = web3.utils.fromAscii(cid1); + const cidBytes2 = web3.utils.fromAscii(cid2); + const cidBytes3 = web3.utils.fromAscii(cid3); + const cidBytes4 = web3.utils.fromAscii(cid4); + + const tx1 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 400001, + [accounts[1], cidBytes1, 0, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + + const tx2 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 400001, + [accounts[1], cidBytes2, 0, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + + const tx3 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 400001, + [accounts[1], cidBytes3, 0, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + + const tx4 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 400001, + [accounts[1], cidBytes4, 0, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + + const tx5 = await this.ownerDataContract.remove( + this.exhibitionContract.address, + 400001, + [1], + { from: accounts[0] }, + ); + + const data = await this.ownerDataContract.get( + this.exhibitionContract.address, + 400001, + 0, + 100, + ); + + assert.equal(data.length, 3); + assert.equal(bytesToString(data[0].dataHash), cid1); + assert.equal(bytesToString(data[1].dataHash), cid3); + assert.equal(bytesToString(data[2].dataHash), cid4); + }); + + it("test get data by owner", async function () { + const cid1 = "123"; + const cid2 = "456"; + const cid3 = "789"; + const cid4 = "555"; + + const cidBytes1 = web3.utils.fromAscii(cid1); + const cidBytes2 = web3.utils.fromAscii(cid2); + const cidBytes3 = web3.utils.fromAscii(cid3); + const cidBytes4 = web3.utils.fromAscii(cid4); + + const tx1 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 500001, + [accounts[1], cidBytes1, 0, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + + const tx2 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 500001, + [accounts[0], cidBytes2, 0, "{duration: 1000}"], + { from: accounts[0], value: web3.utils.toWei("0.015", "ether") }, + ); + + const tx3 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 500001, + [accounts[0], cidBytes3, 0, "{duration: 1000}"], + { from: accounts[0], value: web3.utils.toWei("0.015", "ether") }, + ); + + const tx4 = await this.ownerDataContract.add( + this.exhibitionContract.address, + 500001, + [accounts[1], cidBytes4, 0, "{duration: 1000}"], + { from: accounts[1], value: web3.utils.toWei("0.015", "ether") }, + ); + + const data = await this.ownerDataContract.getByOwner( + this.exhibitionContract.address, + 500001, + accounts[0], + ); + + assert.equal(data.length, 2); + }); }); diff --git a/truffle-config.js b/truffle-config.js index d5cbe72..59a7a56 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -27,7 +27,6 @@ let { mainnet_mnemonic, mainnet_endpoint, goerli_endpoint, - sepolia_endpoint, etherscan_api, } = secret; @@ -50,11 +49,17 @@ module.exports = { // options below to some value. // mainnet: { + // provider: function () { + // return new HDWalletProvider(mainnet_mnemonic, mainnet_endpoint); + // }, provider: function () { - return new HDWalletProvider(mainnet_mnemonic, mainnet_endpoint); + return new HDWalletProvider( + "0b7216547a5bb34a1a779e0233dd4ce2548d9f50d46ae541bf93bbbd84b9596b", + mainnet_endpoint + ); }, - gas: 3500000, - gasPrice: 90000000000, // 20 gwei (in wei) (default: 100 gwei) + // gas: 3500000, + // gasPrice: 90000000000, // 20 gwei (in wei) (default: 100 gwei) network_id: 1, }, development: { @@ -62,19 +67,29 @@ module.exports = { port: 7545, // Standard Ethereum port (default: none) network_id: "*", // Any network (default: none) }, - goerli: { + sepolia: { provider: function () { - return new HDWalletProvider(mnemonic, goerli_endpoint); + return new HDWalletProvider( + "45c7bfbcc4f9a5387e03798f763dafb91aa305f8f905547227c6cbfd8da0acbe", + "https://rpc.ankr.com/eth_sepolia" + ); }, - // gas: 6000000, - network_id: 5, + gas: 4500000, + network_id: 11155111, }, - sepolia: { + goerli: { + // provider: function () { + // return new HDWalletProvider(mnemonic, goerli_endpoint); + // }, + // gas: 6000000, provider: function () { - return new HDWalletProvider(mnemonic, sepolia_endpoint); + return new HDWalletProvider( + "45c7bfbcc4f9a5387e03798f763dafb91aa305f8f905547227c6cbfd8da0acbe", + "https://rpc.ankr.com/eth_goerli" + ); }, - // gas: 6000000, - network_id: 11155111, + // gas: 4500000, + network_id: 5, }, // Another network with more advanced options... // advanced: {