From d83e7dfa9a384860a1910d219fc90da65474420b Mon Sep 17 00:00:00 2001 From: robertu <4065233+robertu7@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:36:30 +0700 Subject: [PATCH] feat(billboard): add `getBidderBids` --- .gas-snapshot | 119 +++++++++++++------------ src/Billboard/Billboard.sol | 34 +++++-- src/Billboard/BillboardRegistry.sol | 11 +++ src/Billboard/IBillboard.sol | 20 +++++ src/Billboard/IBillboardRegistry.sol | 10 +++ src/test/Billboard/BillboardTest.t.sol | 44 +++++++++ 6 files changed, 173 insertions(+), 65 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 3c2652c..b3c2aec 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -9,52 +9,53 @@ ACLManagerTest:testGrantRole() (gas: 23547) ACLManagerTest:testRenounceRole() (gas: 27841) ACLManagerTest:testRoles() (gas: 15393) ACLManagerTest:testTransferRole() (gas: 21528) -BillboardTest:testApproveAndTransfer() (gas: 258043) -BillboardTest:testCalculateTax() (gas: 539888) -BillboardTest:testCannnotWithdrawTaxIfZero() (gas: 20184) -BillboardTest:testCannotApproveByAttacker() (gas: 224303) -BillboardTest:testCannotCalculateTax() (gas: 218949) -BillboardTest:testCannotClearAuctionIfAuctionNotEnded() (gas: 255688) -BillboardTest:testCannotClearAuctionIfClosed() (gas: 252836) -BillboardTest:testCannotClearAuctionIfNoBid() (gas: 260876) -BillboardTest:testCannotGetBlockFromEpoch() (gas: 8622) -BillboardTest:testCannotGetEpochFromBlock() (gas: 18458) -BillboardTest:testCannotPlaceBidIfAuctionEnded() (gas: 278692) -BillboardTest:testCannotPlaceBidIfClosed() (gas: 252735) -BillboardTest:testCannotPlaceBidIfNotWhitelisted() (gas: 463932) -BillboardTest:testCannotSafeTransferByAttacker() (gas: 221547) -BillboardTest:testCannotSetBoardByAttacker() (gas: 229522) -BillboardTest:testCannotSetBoardByOwner() (gas: 361740) -BillboardTest:testCannotSetClosedByAttacker() (gas: 228498) -BillboardTest:testCannotSetWhitelistByAttacker() (gas: 228686) -BillboardTest:testCannotTransferByOperator() (gas: 226704) -BillboardTest:testCannotTransferToZeroAddress() (gas: 222191) -BillboardTest:testCannotUpgradeRegistryByAttacker() (gas: 9127) -BillboardTest:testCannotWithdrawBidIfAuctionNotEndedOrCleared(uint96) (runs: 256, μ: 606433, ~: 606433) -BillboardTest:testCannotWithdrawBidIfNotFound(uint96) (runs: 256, μ: 723940, ~: 723940) -BillboardTest:testCannotWithdrawBidIfWon(uint96) (runs: 256, μ: 975429, ~: 975429) -BillboardTest:testCannotWithdrawBidTwice(uint96) (runs: 256, μ: 1059869, ~: 1059869) -BillboardTest:testClearAuction(uint96) (runs: 256, μ: 705310, ~: 705310) -BillboardTest:testClearAuctionIfAlreadyCleared() (gas: 712435) -BillboardTest:testClearAuctions() (gas: 1262152) -BillboardTest:testGetBids(uint8,uint8,uint8) (runs: 256, μ: 7753801, ~: 5910727) -BillboardTest:testGetBlockFromEpoch() (gas: 17135) -BillboardTest:testGetEpochFromBlock() (gas: 18167) -BillboardTest:testGetTokenURI() (gas: 391429) -BillboardTest:testMintBoard() (gas: 585710) -BillboardTest:testPlaceBid(uint96) (runs: 256, μ: 788827, ~: 789449) -BillboardTest:testPlaceBidWithHigherPrice(uint96) (runs: 256, μ: 970451, ~: 970458) -BillboardTest:testPlaceBidWithSamePrices(uint96) (runs: 256, μ: 857635, ~: 858568) -BillboardTest:testPlaceBidZeroPrice() (gas: 406133) -BillboardTest:testSafeTransferByOperator() (gas: 235236) -BillboardTest:testSetBidURIs() (gas: 633240) -BillboardTest:testSetBoardByCreator() (gas: 342216) -BillboardTest:testSetClosed() (gas: 240886) -BillboardTest:testSetWhitelist() (gas: 245315) -BillboardTest:testUpgradeRegistry() (gas: 3495108) -BillboardTest:testWithdrawBid(uint96) (runs: 256, μ: 1047812, ~: 1047812) -BillboardTest:testWithdrawBidIfClosed(uint96) (runs: 256, μ: 670397, ~: 670397) -BillboardTest:testWithdrawTax(uint96) (runs: 256, μ: 710598, ~: 710598) +BillboardTest:testApproveAndTransfer() (gas: 258088) +BillboardTest:testCalculateTax() (gas: 539668) +BillboardTest:testCannnotWithdrawTaxIfZero() (gas: 20140) +BillboardTest:testCannotApproveByAttacker() (gas: 224347) +BillboardTest:testCannotCalculateTax() (gas: 218883) +BillboardTest:testCannotClearAuctionIfAuctionNotEnded() (gas: 255578) +BillboardTest:testCannotClearAuctionIfClosed() (gas: 252770) +BillboardTest:testCannotClearAuctionIfNoBid() (gas: 260744) +BillboardTest:testCannotGetBlockFromEpoch() (gas: 8600) +BillboardTest:testCannotGetEpochFromBlock() (gas: 18393) +BillboardTest:testCannotPlaceBidIfAuctionEnded() (gas: 278628) +BillboardTest:testCannotPlaceBidIfClosed() (gas: 252692) +BillboardTest:testCannotPlaceBidIfNotWhitelisted() (gas: 463880) +BillboardTest:testCannotSafeTransferByAttacker() (gas: 221503) +BillboardTest:testCannotSetBoardByAttacker() (gas: 229566) +BillboardTest:testCannotSetBoardByOwner() (gas: 362072) +BillboardTest:testCannotSetClosedByAttacker() (gas: 228454) +BillboardTest:testCannotSetWhitelistByAttacker() (gas: 228664) +BillboardTest:testCannotTransferByOperator() (gas: 226749) +BillboardTest:testCannotTransferToZeroAddress() (gas: 222236) +BillboardTest:testCannotUpgradeRegistryByAttacker() (gas: 9105) +BillboardTest:testCannotWithdrawBidIfAuctionNotEndedOrCleared(uint96) (runs: 256, μ: 630648, ~: 630648) +BillboardTest:testCannotWithdrawBidIfNotFound(uint96) (runs: 256, μ: 748156, ~: 748156) +BillboardTest:testCannotWithdrawBidIfWon(uint96) (runs: 256, μ: 1024014, ~: 1024014) +BillboardTest:testCannotWithdrawBidTwice(uint96) (runs: 256, μ: 1108455, ~: 1108455) +BillboardTest:testClearAuction(uint96) (runs: 256, μ: 729526, ~: 729526) +BillboardTest:testClearAuctionIfAlreadyCleared() (gas: 736674) +BillboardTest:testClearAuctions() (gas: 1310672) +BillboardTest:testGetBidderBids(uint8,uint8,uint8) (runs: 256, μ: 1503234, ~: 1134296) +BillboardTest:testGetBids(uint8,uint8,uint8) (runs: 257, μ: 8185787, ~: 6273841) +BillboardTest:testGetBlockFromEpoch() (gas: 16849) +BillboardTest:testGetEpochFromBlock() (gas: 17968) +BillboardTest:testGetTokenURI() (gas: 391497) +BillboardTest:testMintBoard() (gas: 585644) +BillboardTest:testPlaceBid(uint96) (runs: 257, μ: 837566, ~: 838186) +BillboardTest:testPlaceBidWithHigherPrice(uint96) (runs: 257, μ: 1009249, ~: 1009253) +BillboardTest:testPlaceBidWithSamePrices(uint96) (runs: 257, μ: 906266, ~: 907196) +BillboardTest:testPlaceBidZeroPrice() (gas: 430502) +BillboardTest:testSafeTransferByOperator() (gas: 235301) +BillboardTest:testSetBidURIs() (gas: 657544) +BillboardTest:testSetBoardByCreator() (gas: 342349) +BillboardTest:testSetClosed() (gas: 240866) +BillboardTest:testSetWhitelist() (gas: 245316) +BillboardTest:testUpgradeRegistry() (gas: 3655978) +BillboardTest:testWithdrawBid(uint96) (runs: 256, μ: 1096420, ~: 1096420) +BillboardTest:testWithdrawBidIfClosed(uint96) (runs: 256, μ: 694679, ~: 694679) +BillboardTest:testWithdrawTax(uint96) (runs: 256, μ: 734859, ~: 734859) CurationTest:testCannotCurateERC20CurateZeroAmount() (gas: 12194) CurationTest:testCannotCurateERC20EmptyURI() (gas: 15797) CurationTest:testCannotCurateERC20IfNotApproval() (gas: 21624) @@ -72,8 +73,8 @@ DistributionTest:testCannotClaimIfAlreadyClaimed() (gas: 284835) DistributionTest:testCannotClaimIfInsufficientBalance() (gas: 394264) DistributionTest:testCannotClaimIfInvalidProof() (gas: 245236) DistributionTest:testCannotClaimIfInvalidTreeId() (gas: 243332) -DistributionTest:testCannotDropIfInsufficientAllowance(uint256) (runs: 256, μ: 212269, ~: 212284) -DistributionTest:testCannotDropIfInsufficientBalance(uint256) (runs: 256, μ: 214708, ~: 214740) +DistributionTest:testCannotDropIfInsufficientAllowance(uint256) (runs: 257, μ: 212269, ~: 212283) +DistributionTest:testCannotDropIfInsufficientBalance(uint256) (runs: 257, μ: 214708, ~: 214740) DistributionTest:testCannotDropIfZeroAmount() (gas: 148793) DistributionTest:testCannotDropTwiceWithSameTreeId() (gas: 307260) DistributionTest:testCannotSetAdminByAdmin() (gas: 17334) @@ -86,14 +87,14 @@ DistributionTest:testSetAdmin() (gas: 20239) DistributionTest:testSweep() (gas: 253087) LogbookNFTSVGTest:testTokenURI(uint8,uint8,uint16) (runs: 256, μ: 2613180, ~: 1746428) LogbookTest:testClaim() (gas: 135608) -LogbookTest:testDonate(uint96) (runs: 256, μ: 156549, ~: 156936) -LogbookTest:testDonateWithCommission(uint96,uint96) (runs: 256, μ: 146644, ~: 140444) -LogbookTest:testFork(uint96,string) (runs: 256, μ: 452537, ~: 453928) -LogbookTest:testForkRecursively(uint8,uint96) (runs: 256, μ: 5351224, ~: 1801537) -LogbookTest:testForkWithCommission(uint96,string,uint256) (runs: 256, μ: 342465, ~: 257636) +LogbookTest:testDonate(uint96) (runs: 257, μ: 156550, ~: 156936) +LogbookTest:testDonateWithCommission(uint96,uint96) (runs: 257, μ: 146620, ~: 140444) +LogbookTest:testFork(uint96,string) (runs: 257, μ: 452543, ~: 453928) +LogbookTest:testForkRecursively(uint8,uint96) (runs: 257, μ: 5348891, ~: 1801537) +LogbookTest:testForkWithCommission(uint96,string,uint256) (runs: 257, μ: 342132, ~: 257636) LogbookTest:testMulticall() (gas: 284999) LogbookTest:testPublicSale() (gas: 207337) -LogbookTest:testPublish(string) (runs: 256, μ: 264065, ~: 263590) +LogbookTest:testPublish(string) (runs: 257, μ: 264063, ~: 263590) LogbookTest:testPublishEn1000() (gas: 243477) LogbookTest:testPublishEn140() (gas: 221241) LogbookTest:testPublishEn200() (gas: 222826) @@ -112,7 +113,7 @@ LogbookTest:testPublishZh5000() (gas: 607690) LogbookTest:testSetDescription() (gas: 140760) LogbookTest:testSetForkPrice() (gas: 153925) LogbookTest:testSetTitle() (gas: 168680) -LogbookTest:testSplitRoyalty(uint8,uint8,uint96) (runs: 256, μ: 1959072, ~: 965338) +LogbookTest:testSplitRoyalty(uint8,uint8,uint96) (runs: 257, μ: 1963054, ~: 965338) LogbookTest:testWithdraw() (gas: 7284400) SnapperTest:testCannotInitRegionByNotOwner() (gas: 11365) SnapperTest:testCannotReInitRegion() (gas: 14373) @@ -120,10 +121,10 @@ SnapperTest:testCannotTakeSnapshotBeforeInit() (gas: 15717) SnapperTest:testCannotTakeSnapshotByNotOwner() (gas: 12478) SnapperTest:testCannotTakeSnapshotWrongLastBlock() (gas: 49242) SnapperTest:testCannotTakeSnapshotWrongSnapshotBlock() (gas: 23899) -SnapperTest:testInitRegion(uint256) (runs: 256, μ: 114408, ~: 114408) +SnapperTest:testInitRegion(uint256) (runs: 257, μ: 114408, ~: 114408) SnapperTest:testTakeSnapshot() (gas: 47831) TheSpaceTest:testBatchBid() (gas: 695308) -TheSpaceTest:testBatchSetPixels(uint16,uint8) (runs: 256, μ: 371399, ~: 372904) +TheSpaceTest:testBatchSetPixels(uint16,uint8) (runs: 257, μ: 371405, ~: 372904) TheSpaceTest:testBidDefaultedToken() (gas: 413399) TheSpaceTest:testBidExistingToken() (gas: 360023) TheSpaceTest:testBidNewToken() (gas: 303729) @@ -160,11 +161,11 @@ TheSpaceTest:testSetColor() (gas: 331348) TheSpaceTest:testSetMintTax() (gas: 271715) TheSpaceTest:testSetPixel(uint256) (runs: 256, μ: 403816, ~: 403816) TheSpaceTest:testSetPrice(uint256) (runs: 256, μ: 304652, ~: 304652) -TheSpaceTest:testSetPriceByOperator(uint96) (runs: 256, μ: 354785, ~: 354785) +TheSpaceTest:testSetPriceByOperator(uint96) (runs: 257, μ: 354785, ~: 354785) TheSpaceTest:testSetPriceTooHigh() (gas: 314504) TheSpaceTest:testSetTaxRate() (gas: 347951) TheSpaceTest:testSetTokenImageURI() (gas: 355813) -TheSpaceTest:testSetTotalSupply(uint256) (runs: 256, μ: 352202, ~: 352208) +TheSpaceTest:testSetTotalSupply(uint256) (runs: 257, μ: 352202, ~: 352208) TheSpaceTest:testSetTreasuryShare() (gas: 384288) TheSpaceTest:testSettleTax() (gas: 339465) TheSpaceTest:testTaxCalculation() (gas: 402405) diff --git a/src/Billboard/Billboard.sol b/src/Billboard/Billboard.sol index d784302..6a719fb 100644 --- a/src/Billboard/Billboard.sol +++ b/src/Billboard/Billboard.sol @@ -289,25 +289,47 @@ contract Billboard is IBillboard { ) external view returns (uint256 total, uint256 limit, uint256 offset, IBillboardRegistry.Bid[] memory bids) { uint256 _total = registry.getBidCount(tokenId_, epoch_); - if (limit_ == 0) { + if (limit_ == 0 || offset_ >= _total) { return (_total, limit_, offset_, new IBillboardRegistry.Bid[](0)); } - if (offset_ >= _total) { + uint256 _left = _total - offset_; + uint256 _size = _left > limit_ ? limit_ : _left; + + bids = new IBillboardRegistry.Bid[](_size); + + for (uint256 i = 0; i < _size; i++) { + address _bidder = registry.bidders(tokenId_, epoch_, offset_ + i); + bids[i] = registry.getBid(tokenId_, epoch_, _bidder); + } + + return (_total, limit_, offset_, bids); + } + + /// @inheritdoc IBillboard + function getBidderBids( + uint256 tokenId_, + address bidder_, + uint256 limit_, + uint256 offset_ + ) external view returns (uint256 total, uint256 limit, uint256 offset, IBillboardRegistry.Bid[] memory bids) { + uint256 _total = registry.getBidderBidCount(tokenId_, bidder_); + + if (limit_ == 0 || offset_ >= _total) { return (_total, limit_, offset_, new IBillboardRegistry.Bid[](0)); } uint256 _left = _total - offset_; uint256 _size = _left > limit_ ? limit_ : _left; - IBillboardRegistry.Bid[] memory _bids = new IBillboardRegistry.Bid[](_size); + bids = new IBillboardRegistry.Bid[](_size); for (uint256 i = 0; i < _size; i++) { - address _bidder = registry.bidders(tokenId_, epoch_, offset_ + i); - _bids[i] = registry.getBid(tokenId_, epoch_, _bidder); + uint256 _epoch = registry.bidderBids(tokenId_, bidder_, offset_ + i); + bids[i] = registry.getBid(tokenId_, _epoch, bidder_); } - return (_total, limit_, offset_, _bids); + return (_total, limit_, offset_, bids); } /// @inheritdoc IBillboard diff --git a/src/Billboard/BillboardRegistry.sol b/src/Billboard/BillboardRegistry.sol index fd22883..5b3c6d2 100644 --- a/src/Billboard/BillboardRegistry.sol +++ b/src/Billboard/BillboardRegistry.sol @@ -29,6 +29,9 @@ contract BillboardRegistry is IBillboardRegistry, ERC721 { // tokenId => epoch => bidder => Bid mapping(uint256 => mapping(uint256 => mapping(address => Bid))) public bids; + // tokenId => address => epoches + mapping(uint256 => mapping(address => uint256[])) public bidderBids; + // board creator => TaxTreasury mapping(address => TaxTreasury) public taxTreasury; @@ -131,6 +134,11 @@ contract BillboardRegistry is IBillboardRegistry, ERC721 { count = bidders[tokenId_][epoch_].length; } + /// @inheritdoc IBillboardRegistry + function getBidderBidCount(uint256 tokenId_, address bidder_) external view returns (uint256 count) { + count = bidderBids[tokenId_][bidder_].length; + } + /// @inheritdoc IBillboardRegistry function newBid( uint256 tokenId_, @@ -155,6 +163,9 @@ contract BillboardRegistry is IBillboardRegistry, ERC721 { // add to auction bids bids[tokenId_][epoch_][bidder_] = _bid; + // add to bidder's bids + bidderBids[tokenId_][bidder_].push(epoch_); + // add to auction bidders if new bid bidders[tokenId_][epoch_].push(bidder_); diff --git a/src/Billboard/IBillboard.sol b/src/Billboard/IBillboard.sol index 44f0ed8..ef00f6d 100644 --- a/src/Billboard/IBillboard.sol +++ b/src/Billboard/IBillboard.sol @@ -220,6 +220,26 @@ interface IBillboard { uint256 offset_ ) external view returns (uint256 total, uint256 limit, uint256 offset, IBillboardRegistry.Bid[] memory bids); + /** + * @notice Get all bids of bidder by token ID. + * + * @param tokenId_ Token ID. + * @param bidder_ Address of bidder. + * @param limit_ Limit of returned bids. + * @param offset_ Offset of returned bids. + * + * @return total Total number of bids. + * @return limit Limit of returned bids. + * @return offset Offset of returned bids. + * @return bids Bids. + */ + function getBidderBids( + uint256 tokenId_, + address bidder_, + uint256 limit_, + uint256 offset_ + ) external view returns (uint256 total, uint256 limit, uint256 offset, IBillboardRegistry.Bid[] memory bids); + /** * @notice Withdraw bid that were not won by auction id; * diff --git a/src/Billboard/IBillboardRegistry.sol b/src/Billboard/IBillboardRegistry.sol index c9eae4e..5a270bc 100644 --- a/src/Billboard/IBillboardRegistry.sol +++ b/src/Billboard/IBillboardRegistry.sol @@ -203,6 +203,16 @@ interface IBillboardRegistry is IERC721 { */ function getBidCount(uint256 tokenId_, uint256 epoch_) external view returns (uint256 count); + /** + * @notice Get the count of bidder bids + * + * @param tokenId_ Token ID of a board. + * @param bidder_ Bidder of an auction. + * + * @return count Count of bids. + */ + function getBidderBidCount(uint256 tokenId_, address bidder_) external view returns (uint256 count); + /** * @notice Create a bid * diff --git a/src/test/Billboard/BillboardTest.t.sol b/src/test/Billboard/BillboardTest.t.sol index 00742b6..f569e0a 100644 --- a/src/test/Billboard/BillboardTest.t.sol +++ b/src/test/Billboard/BillboardTest.t.sol @@ -574,6 +574,50 @@ contract BillboardTest is BillboardTestBase { } } + function testGetBidderBids(uint8 _bidCount, uint8 _limit, uint8 _offset) public { + vm.assume(_bidCount > 0); + vm.assume(_bidCount <= 10); + vm.assume(_limit <= _bidCount); + vm.assume(_offset <= _limit); + + (uint256 _tokenId, IBillboardRegistry.Board memory _board) = _mintBoard(); + + for (uint8 i = 0; i < _bidCount; i++) { + uint256 _epoch = operator.getEpochFromBlock(_board.startedAt, block.number, _board.epochInterval) + i; + + vm.prank(ADMIN); + operator.setWhitelist(_tokenId, USER_A, true); + + uint256 _price = 1 ether + i; + uint256 _tax = operator.calculateTax(_tokenId, _price); + uint256 _totalAmount = _price + _tax; + + deal(address(usdt), USER_A, _totalAmount); + vm.startPrank(USER_A); + usdt.approve(address(operator), _totalAmount); + operator.placeBid(_tokenId, _epoch, _price); + vm.stopPrank(); + } + + // get bidder bids + (uint256 _t, uint256 _l, uint256 _o, IBillboardRegistry.Bid[] memory _bids) = operator.getBidderBids( + _tokenId, + USER_A, + _limit, + _offset + ); + uint256 _left = _t - _offset; + uint256 _size = _left > _limit ? _limit : _left; + assertEq(_t, _bidCount); + assertEq(_l, _limit); + assertEq(_bids.length, _size); + assertEq(_o, _offset); + for (uint256 i = 0; i < _size; i++) { + uint256 _price = 1 ether + _offset + i; + assertEq(_bids[i].price, _price); + } + } + function testWithdrawBid(uint96 _price) public { vm.assume(_price > 0.001 ether);