diff --git a/.gas-snapshot b/.gas-snapshot index 63d8925..a834f82 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -11,16 +11,16 @@ ACLManagerTest:testRoles() (gas: 15393) ACLManagerTest:testTransferRole() (gas: 21528) BillboardTest:testAddToWhitelist() (gas: 35114) BillboardTest:testApproveAndTransfer() (gas: 162512) -BillboardTest:testCalculateTax() (gas: 21782) -BillboardTest:testCannnotWithdrawTaxIfSmallAmount(uint8) (runs: 256, μ: 418269, ~: 424925) -BillboardTest:testCannnotWithdrawTaxIfZero() (gas: 379035) +BillboardTest:testCalculateTax() (gas: 21760) +BillboardTest:testCannnotWithdrawTaxIfSmallAmount(uint8) (runs: 256, μ: 519606, ~: 524472) +BillboardTest:testCannnotWithdrawTaxIfZero() (gas: 490270) BillboardTest:testCannotAddToWhitelistByAttacker() (gas: 9037) -BillboardTest:testCannotApproveByAttacker() (gas: 130271) -BillboardTest:testCannotClearAuctionIfAuctionNotEnded() (gas: 579292) +BillboardTest:testCannotApproveByAttacker() (gas: 130304) +BillboardTest:testCannotClearAuctionIfAuctionNotEnded() (gas: 699798) BillboardTest:testCannotClearAuctionOnNewBoard() (gas: 136253) BillboardTest:testCannotMintBoardByAttacker() (gas: 13321) -BillboardTest:testCannotPlaceBidByAttacker() (gas: 138273) -BillboardTest:testCannotPlaceBidTwice(uint96) (runs: 256, μ: 623750, ~: 630370) +BillboardTest:testCannotPlaceBidByAttacker() (gas: 246276) +BillboardTest:testCannotPlaceBidTwice(uint96) (runs: 256, μ: 749693, ~: 754800) BillboardTest:testCannotRemoveToWhitelistByAttacker() (gas: 9104) BillboardTest:testCannotSafeTransferByAttacker() (gas: 127438) BillboardTest:testCannotSetBoardProprtiesByAttacker() (gas: 157292) @@ -29,35 +29,35 @@ BillboardTest:testCannotSetTaxRateByAttacker() (gas: 9006) BillboardTest:testCannotTransferByOperator() (gas: 132771) BillboardTest:testCannotTransferToZeroAddress() (gas: 128258) BillboardTest:testCannotUpgradeRegistryByAttacker() (gas: 9128) -BillboardTest:testCannotWithBidTwice(uint96) (runs: 256, μ: 901993, ~: 901993) -BillboardTest:testCannotWithdrawBidIfAuctionNotCleared(uint96) (runs: 256, μ: 742413, ~: 742413) -BillboardTest:testCannotWithdrawBidIfAuctionNotEnded(uint96) (runs: 256, μ: 618541, ~: 618541) -BillboardTest:testCannotWithdrawBidIfNotFound() (gas: 414332) -BillboardTest:testCannotWithdrawBidIfWon(uint96) (runs: 256, μ: 710358, ~: 710358) +BillboardTest:testCannotWithBidTwice(uint96) (runs: 256, μ: 1079596, ~: 1079596) +BillboardTest:testCannotWithdrawBidIfAuctionNotCleared(uint96) (runs: 256, μ: 910976, ~: 910976) +BillboardTest:testCannotWithdrawBidIfAuctionNotEnded(uint96) (runs: 256, μ: 725647, ~: 725647) +BillboardTest:testCannotWithdrawBidIfNotFound() (gas: 428057) +BillboardTest:testCannotWithdrawBidIfWon(uint96) (runs: 256, μ: 833971, ~: 833971) BillboardTest:testCannotWithdrawTaxByAttacker() (gas: 16677) -BillboardTest:testClearAuctionIfAuctionEnded() (gas: 623626) -BillboardTest:testClearAuctionsIfAuctionEnded() (gas: 1156359) -BillboardTest:testGetBids(uint8,uint8,uint8) (runs: 256, μ: 2878685, ~: 1412103) -BillboardTest:testGetTokenURI() (gas: 154980) +BillboardTest:testClearAuctionIfAuctionEnded() (gas: 743933) +BillboardTest:testClearAuctionsIfAuctionEnded() (gas: 1376419) +BillboardTest:testGetBids(uint8,uint8,uint8) (runs: 256, μ: 4716997, ~: 2077258) +BillboardTest:testGetTokenURI() (gas: 154936) BillboardTest:testMintBoard() (gas: 225541) BillboardTest:testMintBoardByWhitelist() (gas: 154942) BillboardTest:testMintBoardIfOpened() (gas: 145715) -BillboardTest:testPlaceBidByWhitelist() (gas: 461646) -BillboardTest:testPlaceBidIfAuctionEnded() (gas: 905458) -BillboardTest:testPlaceBidOnNewBoard(uint96) (runs: 256, μ: 510257, ~: 521365) -BillboardTest:testPlaceBidWithHigherPrice(uint96) (runs: 256, μ: 736177, ~: 744573) -BillboardTest:testPlaceBidWithSamePrices(uint96) (runs: 256, μ: 735897, ~: 748506) -BillboardTest:testPlaceBidZeroPrice() (gas: 355447) +BillboardTest:testPlaceBidByWhitelist() (gas: 579075) +BillboardTest:testPlaceBidIfAuctionEnded() (gas: 1090505) +BillboardTest:testPlaceBidOnNewBoard(uint96) (runs: 256, μ: 619050, ~: 635095) +BillboardTest:testPlaceBidWithHigherPrice(uint96) (runs: 256, μ: 904667, ~: 913141) +BillboardTest:testPlaceBidWithSamePrices(uint96) (runs: 256, μ: 907776, ~: 917960) +BillboardTest:testPlaceBidZeroPrice() (gas: 376910) BillboardTest:testRemoveToWhitelist() (gas: 23207) BillboardTest:testSafeTransferByOperator() (gas: 141237) -BillboardTest:testSetBoardProperties() (gas: 305883) -BillboardTest:testSetBoardPropertiesAfterTransfer() (gas: 335509) +BillboardTest:testSetBoardProperties() (gas: 305950) +BillboardTest:testSetBoardPropertiesAfterTransfer() (gas: 335477) BillboardTest:testSetIsOpened() (gas: 22661) -BillboardTest:testSetTaxRate() (gas: 22909) -BillboardTest:testSomethin() (gas: 1624285) -BillboardTest:testUpgradeRegistry() (gas: 2935587) -BillboardTest:testWithdrawBid(uint96) (runs: 256, μ: 903495, ~: 903495) -BillboardTest:testWithdrawTax(uint96) (runs: 256, μ: 500488, ~: 500488) +BillboardTest:testSetTaxRate() (gas: 22887) +BillboardTest:testSomethin() (gas: 1672218) +BillboardTest:testUpgradeRegistry() (gas: 3038450) +BillboardTest:testWithdrawBid(uint96) (runs: 256, μ: 1081104, ~: 1081104) +BillboardTest:testWithdrawTax(uint96) (runs: 256, μ: 597534, ~: 597534) CurationTest:testCannotCurateERC20CurateZeroAmount() (gas: 12194) CurationTest:testCannotCurateERC20EmptyURI() (gas: 15797) CurationTest:testCannotCurateERC20IfNotApproval() (gas: 21624) @@ -71,8 +71,8 @@ CurationTest:testCannotCurateNativeTokenZeroAddress() (gas: 16488) CurationTest:testERC20Curation() (gas: 59908) CurationTest:testNativeTokenCuration() (gas: 60085) CurationTest:testNativeTokenCurationToContractAcceptor() (gas: 37466) -DistributionTest:testCannotDropIfInsufficientAllowance(uint256) (runs: 256, μ: 158669, ~: 158683) -DistributionTest:testCannotDropIfInsufficientBalance(uint256) (runs: 256, μ: 160867, ~: 161105) +DistributionTest:testCannotDropIfInsufficientAllowance(uint256) (runs: 256, μ: 158670, ~: 158683) +DistributionTest:testCannotDropIfInsufficientBalance(uint256) (runs: 256, μ: 160868, ~: 161107) DistributionTest:testCannotSetAdminByAdmin() (gas: 17364) DistributionTest:testCannotSetAdminByAttacker() (gas: 11111) DistributionTest:testClaim() (gas: 402817) diff --git a/src/Billboard/Billboard.sol b/src/Billboard/Billboard.sol index 17f85c2..84a81cd 100644 --- a/src/Billboard/Billboard.sol +++ b/src/Billboard/Billboard.sol @@ -1,6 +1,8 @@ //SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + import "./BillboardRegistry.sol"; import "./IBillboard.sol"; import "./IBillboardRegistry.sol"; @@ -13,6 +15,7 @@ contract Billboard is IBillboard { bool public isOpened = false; constructor( + address token_, address payable registry_, uint256 taxRate_, uint64 leaseTerm_, @@ -28,7 +31,7 @@ contract Billboard is IBillboard { } // deploy operator and registry else { - registry = new BillboardRegistry(address(this), taxRate_, leaseTerm_, name_, symbol_); + registry = new BillboardRegistry(token_, address(this), taxRate_, leaseTerm_, name_, symbol_); } } @@ -260,15 +263,7 @@ contract Billboard is IBillboard { function _lockBidPriceAndTax(uint256 amount_) private { // transfer bid price and tax to the registry - (bool _success, ) = address(registry).call{value: amount_}(""); - require(_success, "Transfer failed"); - - // refund if overpaid - uint256 _overpaid = msg.value - amount_; - if (_overpaid > 0) { - (bool _refundSuccess, ) = msg.sender.call{value: _overpaid}(""); - require(_refundSuccess, "Transfer failed"); - } + SafeERC20.safeTransferFrom(registry.token(), msg.sender, address(registry), amount_); } /// @inheritdoc IBillboard diff --git a/src/Billboard/BillboardRegistry.sol b/src/Billboard/BillboardRegistry.sol index 575907b..ce48fb5 100644 --- a/src/Billboard/BillboardRegistry.sol +++ b/src/Billboard/BillboardRegistry.sol @@ -1,6 +1,7 @@ //SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; @@ -14,6 +15,7 @@ contract BillboardRegistry is IBillboardRegistry, ERC721 { Counters.Counter public lastTokenId; + IERC20 public immutable token; uint256 public taxRate; uint64 public leaseTerm; @@ -36,6 +38,7 @@ contract BillboardRegistry is IBillboardRegistry, ERC721 { mapping(address => TaxTreasury) public taxTreasury; constructor( + address token_, address operator_, uint256 taxRate_, uint64 leaseTerm_, @@ -43,6 +46,9 @@ contract BillboardRegistry is IBillboardRegistry, ERC721 { string memory symbol_ ) ERC721(name_, symbol_) { require(operator_ != address(0), "Zero address"); + require(token_ != address(0), "Zero address"); + + token = IERC20(token_); operator = operator_; taxRate = taxRate_; leaseTerm = leaseTerm_; @@ -229,8 +235,8 @@ contract BillboardRegistry is IBillboardRegistry, ERC721 { /// @inheritdoc IBillboardRegistry function transferAmount(address to_, uint256 amount_) external isFromOperator { require(to_ != address(0), "Zero address"); - (bool _success, ) = to_.call{value: amount_}(""); - require(_success, "transfer failed"); + + require(token.transfer(to_, amount_), "Failed token transfer"); } ////////////////////////////// diff --git a/src/Billboard/Distribution.sol b/src/Billboard/Distribution.sol index 86e6a58..7283522 100644 --- a/src/Billboard/Distribution.sol +++ b/src/Billboard/Distribution.sol @@ -99,6 +99,8 @@ contract Distribution is IDistribution, Ownable { function sweep(uint256 treeId_, address target_) external isFromAdmin { uint256 _balance = balances[treeId_]; + require(_balance > 0, "zero balance"); + // Transfer require(IERC20(token).transfer(target_, _balance), "failed token transfer"); diff --git a/src/test/Billboard/BillboardTest.t.sol b/src/test/Billboard/BillboardTest.t.sol index dc7a121..b077758 100644 --- a/src/test/Billboard/BillboardTest.t.sol +++ b/src/test/Billboard/BillboardTest.t.sol @@ -13,7 +13,14 @@ contract BillboardTest is BillboardTestBase { vm.startPrank(ADMIN); // deploy new operator - Billboard newOperator = new Billboard(payable(registry), TAX_RATE, LEASE_TERM, "Billboard2", "BLBD2"); + Billboard newOperator = new Billboard( + address(usdt), + payable(registry), + TAX_RATE, + LEASE_TERM, + "Billboard2", + "BLBD2" + ); assertEq(newOperator.admin(), ADMIN); assertEq(registry.name(), "Billboard"); // registry is not changed assertEq(registry.symbol(), "BLBD"); // registry is not changed @@ -356,13 +363,13 @@ contract BillboardTest is BillboardTestBase { uint256 _tax = operator.calculateTax(_amount); uint256 _overpaid = 0.1 ether; uint256 _total = _amount + _tax; - vm.deal(USER_A, _total + _overpaid); + deal(address(usdt), USER_A, _total + _overpaid); uint256 _prevNextActionId = registry.nextBoardAuctionId(_tokenId); - uint256 _prevCreatorBalance = ADMIN.balance; - uint256 _prevBidderBalance = USER_A.balance; - uint256 _prevOperatorBalance = address(operator).balance; - uint256 _prevRegistryBalance = address(registry).balance; + uint256 _prevCreatorBalance = usdt.balanceOf(ADMIN); + uint256 _prevBidderBalance = usdt.balanceOf(USER_A); + uint256 _prevOperatorBalance = usdt.balanceOf(address(operator)); + uint256 _prevRegistryBalance = usdt.balanceOf(address(registry)); vm.expectEmit(true, true, true, true); emit IBillboardRegistry.AuctionCreated( @@ -385,13 +392,13 @@ contract BillboardTest is BillboardTestBase { ); vm.prank(USER_A); - operator.placeBid{value: _total + _overpaid}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // check balances - assertEq(ADMIN.balance, _prevCreatorBalance + _amount); - assertEq(USER_A.balance, _prevBidderBalance - _total); - assertEq(address(operator).balance, _prevOperatorBalance); - assertEq(address(registry).balance, _prevRegistryBalance + _tax); + assertEq(usdt.balanceOf(ADMIN), _prevCreatorBalance + _amount); + assertEq(usdt.balanceOf(USER_A), _prevBidderBalance - _total); + assertEq(usdt.balanceOf(address(operator)), _prevOperatorBalance); + assertEq(usdt.balanceOf(address(registry)), _prevRegistryBalance + _tax); // check auction uint256 _nextAuctionId = registry.nextBoardAuctionId(_tokenId); @@ -419,11 +426,11 @@ contract BillboardTest is BillboardTestBase { (uint256 _tokenId3, ) = _mintBoardAndPlaceBid(); vm.startPrank(USER_A); - operator.placeBid{value: 0}(_tokenId, 0); - operator.placeBid{value: 0}(_tokenId2, 0); + operator.placeBid(_tokenId, 0); + operator.placeBid(_tokenId2, 0); vm.startPrank(USER_B); - operator.placeBid{value: 0}(_tokenId3, 0); + operator.placeBid(_tokenId3, 0); vm.roll(block.number + registry.leaseTerm() + 1); uint256[] memory _tokenIds = new uint256[](3); @@ -439,18 +446,18 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; // new auction and new bid with USER_A - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); uint256 _nextAuctionId = registry.nextBoardAuctionId(_tokenId); assertEq(_nextAuctionId, _prevNextAuctionId + 1); IBillboardRegistry.Auction memory _auction = registry.getAuction(_tokenId, _nextAuctionId); assertEq(_auction.highestBidder, USER_A); // new bid with USER_B - vm.deal(USER_B, _total); + deal(address(usdt), USER_B, _total); vm.prank(USER_B); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); _nextAuctionId = registry.nextBoardAuctionId(_tokenId); assertEq(_nextAuctionId, _prevNextAuctionId + 1); // still the same auction _auction = registry.getAuction(_tokenId, _nextAuctionId); @@ -465,7 +472,7 @@ contract BillboardTest is BillboardTestBase { assertEq(_bidB.isWon, false); // check registry balance - assertEq(address(registry).balance, _total * 2); + assertEq(usdt.balanceOf(address(registry)), _total * 2); } function testPlaceBidWithHigherPrice(uint96 _amount) public { @@ -477,9 +484,9 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; // bid with USER_A - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); uint256 _nextAuctionId = registry.nextBoardAuctionId(_tokenId); IBillboardRegistry.Auction memory _auction = registry.getAuction(_tokenId, _nextAuctionId); assertEq(_auction.highestBidder, USER_A); @@ -488,9 +495,9 @@ contract BillboardTest is BillboardTestBase { _amount = _amount * 2; _tax = operator.calculateTax(_amount); _total = _amount + _tax; - vm.deal(USER_B, _total); + deal(address(usdt), USER_B, _total); vm.startPrank(USER_B); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); _auction = registry.getAuction(_tokenId, _nextAuctionId); assertEq(_auction.highestBidder, USER_B); } @@ -499,15 +506,15 @@ contract BillboardTest is BillboardTestBase { uint256 _tokenId = _mintBoard(); vm.startPrank(ADMIN); - uint256 _prevBalance = ADMIN.balance; + uint256 _prevBalance = usdt.balanceOf(ADMIN); - operator.placeBid{value: 0}(_tokenId, 0); + operator.placeBid(_tokenId, 0); // check balances - uint256 _afterBalance = ADMIN.balance; + uint256 _afterBalance = usdt.balanceOf(ADMIN); assertEq(_afterBalance, _prevBalance); - assertEq(address(operator).balance, 0); - assertEq(address(registry).balance, 0); + assertEq(usdt.balanceOf(address(operator)), 0); + assertEq(usdt.balanceOf(address(registry)), 0); // check auction uint256 _nextAuctionId = registry.nextBoardAuctionId(_tokenId); @@ -529,10 +536,10 @@ contract BillboardTest is BillboardTestBase { vm.prank(ADMIN); operator.addToWhitelist(USER_A); - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - operator.placeBid{value: _total}(_tokenId, _amount); - assertEq(USER_A.balance, 0); + operator.placeBid(_tokenId, _amount); + assertEq(usdt.balanceOf(USER_A), 0); } function testPlaceBidIfAuctionEnded() public { @@ -543,8 +550,8 @@ contract BillboardTest is BillboardTestBase { // place a bid with USER_A vm.startPrank(USER_A); - vm.deal(USER_A, _total); - operator.placeBid{value: _total}(_tokenId, _amount); + deal(address(usdt), USER_A, _total); + operator.placeBid(_tokenId, _amount); // check auction uint256 _nextAuctionId = registry.nextBoardAuctionId(_tokenId); @@ -557,8 +564,8 @@ contract BillboardTest is BillboardTestBase { // place a bid with USER_B vm.startPrank(USER_B); - vm.deal(USER_B, _total); - operator.placeBid{value: _total}(_tokenId, _amount); + deal(address(usdt), USER_B, _total); + operator.placeBid(_tokenId, _amount); // check auction uint256 _newNextAuctionId = registry.nextBoardAuctionId(_tokenId); @@ -582,13 +589,13 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; vm.startPrank(USER_A); - vm.deal(USER_A, _total); - operator.placeBid{value: _total}(_tokenId, _amount); - assertEq(USER_A.balance, 0); + deal(address(usdt), USER_A, _total); + operator.placeBid(_tokenId, _amount); + assertEq(usdt.balanceOf(USER_A), 0); - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.expectRevert("Bid already placed"); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); } function testCannotPlaceBidByAttacker() public { @@ -598,9 +605,9 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; vm.startPrank(ATTACKER); - vm.deal(ATTACKER, _total); + deal(address(usdt), ATTACKER, _total); vm.expectRevert("Whitelist"); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); } function testClearAuctionIfAuctionEnded() public { @@ -610,8 +617,8 @@ contract BillboardTest is BillboardTestBase { // place a bid vm.startPrank(USER_A); - vm.deal(USER_A, 0); - operator.placeBid{value: 0}(_tokenId, 0); + deal(address(usdt), USER_A, 0); + operator.placeBid(_tokenId, 0); // clear auction vm.expectEmit(true, true, true, true); @@ -653,12 +660,12 @@ contract BillboardTest is BillboardTestBase { // place bids vm.startPrank(USER_A); - vm.deal(USER_A, 0); - operator.placeBid{value: 0}(_tokenId, 0); + deal(address(usdt), USER_A, 0); + operator.placeBid(_tokenId, 0); vm.startPrank(USER_B); - vm.deal(USER_B, 0); - operator.placeBid{value: 0}(_tokenId2, 0); + deal(address(usdt), USER_B, 0); + operator.placeBid(_tokenId2, 0); // clear auction vm.expectEmit(true, true, true, true); @@ -736,8 +743,8 @@ contract BillboardTest is BillboardTestBase { // place a bid vm.startPrank(USER_A); - vm.deal(USER_A, 0); - operator.placeBid{value: 0}(_tokenId, 0); + deal(address(usdt), USER_A, 0); + operator.placeBid(_tokenId, 0); // try to clear auction vm.expectRevert("Auction not ended"); @@ -766,9 +773,11 @@ contract BillboardTest is BillboardTestBase { uint256 _tax = operator.calculateTax(_amount); uint256 _totalAmount = _amount + _tax; - vm.deal(_bidder, _totalAmount); - vm.prank(_bidder); - operator.placeBid{value: _totalAmount}(_tokenId, _amount); + deal(address(usdt), _bidder, _totalAmount); + vm.startPrank(_bidder); + usdt.approve(address(operator), _totalAmount); + operator.placeBid(_tokenId, _amount); + vm.stopPrank(); } // get bids @@ -834,12 +843,12 @@ contract BillboardTest is BillboardTestBase { operator.addToWhitelist(USER_A); // place a bid and win auction - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); - uint256 _prevRegistryBalance = address(registry).balance; - uint256 _prevAdminBalance = ADMIN.balance; + uint256 _prevRegistryBalance = usdt.balanceOf(address(registry)); + uint256 _prevAdminBalance = usdt.balanceOf(ADMIN); // withdraw tax vm.expectEmit(true, true, true, true); @@ -849,8 +858,8 @@ contract BillboardTest is BillboardTestBase { operator.withdrawTax(); // check balances - assertEq(address(registry).balance, _prevRegistryBalance - _tax); - assertEq(ADMIN.balance, _prevAdminBalance + _tax); + assertEq(usdt.balanceOf(address(registry)), _prevRegistryBalance - _tax); + assertEq(usdt.balanceOf(ADMIN), _prevAdminBalance + _tax); } function testCannnotWithdrawTaxIfZero() public { @@ -860,9 +869,9 @@ contract BillboardTest is BillboardTestBase { operator.addToWhitelist(USER_A); // place a bid and win auction - vm.deal(USER_A, 0); + deal(address(usdt), USER_A, 0); vm.prank(USER_A); - operator.placeBid{value: 0}(_tokenId, 0); + operator.placeBid(_tokenId, 0); vm.prank(ADMIN); vm.expectRevert("Zero amount"); @@ -879,9 +888,9 @@ contract BillboardTest is BillboardTestBase { operator.addToWhitelist(USER_A); // place a bid and win auction - vm.deal(USER_A, _amount); + deal(address(usdt), USER_A, _amount); vm.prank(USER_A); - operator.placeBid{value: _amount}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); vm.prank(ADMIN); vm.expectRevert("Zero amount"); @@ -903,14 +912,14 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; // new auction and new bid with USER_A - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // new bid with USER_B - vm.deal(USER_B, _total); + deal(address(usdt), USER_B, _total); vm.prank(USER_B); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // clear auction vm.roll(block.number + registry.leaseTerm() + 1); @@ -933,7 +942,7 @@ contract BillboardTest is BillboardTestBase { vm.prank(USER_B); operator.withdrawBid(_tokenId, _nextAuctionId); - assertEq(USER_B.balance, _total); + assertEq(usdt.balanceOf(USER_B), _total); } function testCannotWithBidTwice(uint96 _amount) public { @@ -944,14 +953,14 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; // new auction and new bid with USER_A - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // new bid with USER_B - vm.deal(USER_B, _total); + deal(address(usdt), USER_B, _total); vm.prank(USER_B); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // clear auction vm.roll(block.number + registry.leaseTerm() + 1); @@ -965,7 +974,7 @@ contract BillboardTest is BillboardTestBase { // withdraw bid vm.prank(USER_B); operator.withdrawBid(_tokenId, _nextAuctionId); - assertEq(USER_B.balance, _total); + assertEq(usdt.balanceOf(USER_B), _total); // withdraw bid again vm.prank(USER_B); @@ -981,9 +990,9 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; // new auction and new bid with USER_A - vm.deal(USER_A, _total); + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // clear auction vm.roll(block.number + registry.leaseTerm() + 1); @@ -1009,8 +1018,8 @@ contract BillboardTest is BillboardTestBase { // new auction and new bid with USER_A vm.startPrank(USER_A); - vm.deal(USER_A, _total); - operator.placeBid{value: _total}(_tokenId, _amount); + deal(address(usdt), USER_A, _total); + operator.placeBid(_tokenId, _amount); // auction is not ended uint256 _nextAuctionId = registry.nextBoardAuctionId(_tokenId); @@ -1031,14 +1040,14 @@ contract BillboardTest is BillboardTestBase { uint256 _total = _amount + _tax; // new auction and new bid with USER_A + deal(address(usdt), USER_A, _total); vm.prank(USER_A); - vm.deal(USER_A, _total); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // new bid with USER_B - vm.deal(USER_B, _total); + deal(address(usdt), USER_B, _total); vm.prank(USER_B); - operator.placeBid{value: _total}(_tokenId, _amount); + operator.placeBid(_tokenId, _amount); // auction is ended but not cleared uint256 _nextAuctionId = registry.nextBoardAuctionId(_tokenId); diff --git a/src/test/Billboard/BillboardTestBase.t.sol b/src/test/Billboard/BillboardTestBase.t.sol index 2ca6d08..0c3c887 100644 --- a/src/test/Billboard/BillboardTestBase.t.sol +++ b/src/test/Billboard/BillboardTestBase.t.sol @@ -5,6 +5,7 @@ import "forge-std/console.sol"; import "forge-std/Test.sol"; import "forge-std/Vm.sol"; +import {USDT} from "../utils/USDT.sol"; import {Billboard} from "../../Billboard/Billboard.sol"; import {BillboardRegistry} from "../../Billboard/BillboardRegistry.sol"; import {IBillboard} from "../../Billboard/IBillboard.sol"; @@ -13,6 +14,7 @@ import {IBillboardRegistry} from "../../Billboard/IBillboardRegistry.sol"; contract BillboardTestBase is Test { Billboard internal operator; BillboardRegistry internal registry; + USDT internal usdt; uint256 constant TAX_RATE = 1; // 1% per lease term uint64 constant LEASE_TERM = 100; // 100 blocks @@ -30,8 +32,11 @@ contract BillboardTestBase is Test { function setUp() public { vm.startPrank(ADMIN); + // deploy USDT + usdt = new USDT(ADMIN, 0); + // deploy operator & registry - operator = new Billboard(payable(address(0)), TAX_RATE, LEASE_TERM, "Billboard", "BLBD"); + operator = new Billboard(address(usdt), payable(address(0)), TAX_RATE, LEASE_TERM, "Billboard", "BLBD"); registry = operator.registry(); assertEq(operator.admin(), ADMIN); assertEq(registry.operator(), address(operator)); @@ -39,6 +44,17 @@ contract BillboardTestBase is Test { assertEq(registry.symbol(), "BLBD"); vm.stopPrank(); + + // approve USDT + uint256 MAX_ALLOWANCE = type(uint256).max; + vm.prank(ADMIN); + usdt.approve(address(operator), MAX_ALLOWANCE); + vm.prank(USER_A); + usdt.approve(address(operator), MAX_ALLOWANCE); + vm.prank(USER_B); + usdt.approve(address(operator), MAX_ALLOWANCE); + vm.prank(USER_C); + usdt.approve(address(operator), MAX_ALLOWANCE); } function _mintBoard() public returns (uint256 tokenId) { @@ -51,7 +67,7 @@ contract BillboardTestBase is Test { // (new board) ADMIN places first bid and takes the ownership vm.startPrank(ADMIN); - operator.placeBid{value: 0}(tokenId, 0); + operator.placeBid(tokenId, 0); _nextAuctionId = registry.nextBoardAuctionId(tokenId); IBillboardRegistry.Auction memory _auction = registry.getAuction(tokenId, _nextAuctionId); assertEq(_nextAuctionId, 1); diff --git a/src/test/Billboard/DistributionTestBase.t.sol b/src/test/Billboard/DistributionTestBase.t.sol index bae19eb..9fbe571 100644 --- a/src/test/Billboard/DistributionTestBase.t.sol +++ b/src/test/Billboard/DistributionTestBase.t.sol @@ -72,6 +72,7 @@ contract DistributionTestBase is Test { vm.stopPrank(); + // approve USDT uint256 MAX_ALLOWANCE = type(uint256).max; vm.prank(ADMIN); usdt.approve(address(distribution), MAX_ALLOWANCE);