diff --git a/packages/nouns-contracts/.gas-snapshot b/packages/nouns-contracts/.gas-snapshot index 49485f2683..0c32aba6e9 100644 --- a/packages/nouns-contracts/.gas-snapshot +++ b/packages/nouns-contracts/.gas-snapshot @@ -1,12 +1,16 @@ -NounsAuctionHouseV2WarmedUp_GasSnapshot_createBid:test_createOneBid() (gas: 35090) -NounsAuctionHouseV2WarmedUp_GasSnapshot_createBid:test_createTwoBids() (gas: 93677) -NounsAuctionHouseV2WarmedUp_GasSnapshot_createBid:test_settleCurrentAndCreateNewAuction() (gas: 232680) -NounsAuctionHouseV2_GasSnapshot_createBid:test_createOneBid() (gas: 35090) -NounsAuctionHouseV2_GasSnapshot_createBid:test_createTwoBids() (gas: 93677) -NounsAuctionHouseV2_GasSnapshot_createBid:test_settleCurrentAndCreateNewAuction() (gas: 249780) -NounsAuctionHouse_GasSnapshot_createBid:test_createOneBid() (gas: 81474) -NounsAuctionHouse_GasSnapshot_createBid:test_createTwoBids() (gas: 142736) -NounsAuctionHouse_GasSnapshot_createBid:test_settleCurrentAndCreateNewAuction() (gas: 243008) +NounsAuctionHouseV2WarmedUp_GasSnapshot:test_createOneBid() (gas: 34963) +NounsAuctionHouseV2WarmedUp_GasSnapshot:test_createTwoBids() (gas: 93423) +NounsAuctionHouseV2WarmedUp_GasSnapshot:test_settleCurrentAndCreateNewAuction() (gas: 232686) +NounsAuctionHouseV2_GasSnapshot:test_createOneBid() (gas: 34963) +NounsAuctionHouseV2_GasSnapshot:test_createTwoBids() (gas: 93423) +NounsAuctionHouseV2_GasSnapshot:test_settleCurrentAndCreateNewAuction() (gas: 249786) +NounsAuctionHouseV2_HistoricPrices_GasSnapshot:test_getPrices_90() (gas: 310063) +NounsAuctionHouseV2_HistoricPrices_GasSnapshot:test_getPrices_range_90() (gas: 300578) +NounsAuctionHouseV2_HistoricPrices_GasSnapshot:test_getSettlements_90() (gas: 385762) +NounsAuctionHouseV2_HistoricPrices_GasSnapshot:test_getSettlements_range_90() (gas: 378168) +NounsAuctionHouse_GasSnapshot:test_createOneBid() (gas: 81474) +NounsAuctionHouse_GasSnapshot:test_createTwoBids() (gas: 142736) +NounsAuctionHouse_GasSnapshot:test_settleCurrentAndCreateNewAuction() (gas: 243008) NounsDAOLogic_GasSnapshot_V2_propose:test_propose_longDescription() (gas: 528754) NounsDAOLogic_GasSnapshot_V2_propose:test_propose_shortDescription() (gas: 398387) NounsDAOLogic_GasSnapshot_V2_vote:test_castVoteWithReason() (gas: 83585) diff --git a/packages/nouns-contracts/contracts/NounsAuctionHouseV2.sol b/packages/nouns-contracts/contracts/NounsAuctionHouseV2.sol index 83a28b02f4..3994b67175 100644 --- a/packages/nouns-contracts/contracts/NounsAuctionHouseV2.sol +++ b/packages/nouns-contracts/contracts/NounsAuctionHouseV2.sol @@ -32,6 +32,10 @@ import { INounsAuctionHouseV2 } from './interfaces/INounsAuctionHouseV2.sol'; import { INounsToken } from './interfaces/INounsToken.sol'; import { IWETH } from './interfaces/IWETH.sol'; +/** + * @dev The contract inherits from PausableUpgradeable & ReentrancyGuardUpgradeable most of all the keep the same + * storage layout as the NounsAuctionHouse contract + */ contract NounsAuctionHouseV2 is INounsAuctionHouseV2, PausableUpgradeable, @@ -123,11 +127,17 @@ contract NounsAuctionHouseV2 is function createBid(uint256 nounId) external payable override { INounsAuctionHouseV2.AuctionV2 memory _auction = auctionStorage; + (uint192 _reservePrice, uint56 _timeBuffer, uint8 _minBidIncrementPercentage) = ( + reservePrice, + timeBuffer, + minBidIncrementPercentage + ); + require(_auction.nounId == nounId, 'Noun not up for auction'); require(block.timestamp < _auction.endTime, 'Auction expired'); - require(msg.value >= reservePrice, 'Must send at least reservePrice'); + require(msg.value >= _reservePrice, 'Must send at least reservePrice'); require( - msg.value >= _auction.amount + ((_auction.amount * minBidIncrementPercentage) / 100), + msg.value >= _auction.amount + ((_auction.amount * _minBidIncrementPercentage) / 100), 'Must send more than last bid by minBidIncrementPercentage amount' ); @@ -135,12 +145,12 @@ contract NounsAuctionHouseV2 is auctionStorage.bidder = payable(msg.sender); // Extend the auction if the bid was received within `timeBuffer` of the auction end time - bool extended = _auction.endTime - block.timestamp < timeBuffer; + bool extended = _auction.endTime - block.timestamp < _timeBuffer; emit AuctionBid(_auction.nounId, msg.sender, msg.value, extended); if (extended) { - auctionStorage.endTime = _auction.endTime = uint40(block.timestamp + timeBuffer); + auctionStorage.endTime = _auction.endTime = uint40(block.timestamp + _timeBuffer); emit AuctionExtended(_auction.nounId, _auction.endTime); } @@ -343,13 +353,13 @@ contract NounsAuctionHouseV2 is } /** - * @notice Get past auction prices. - * @dev Returns prices in reverse order, meaning settlements[0] will be the most recent auction price. + * @notice Get past auction settlements. + * @dev Returns settlements in reverse order, meaning settlements[0] will be the most recent auction price. * @param auctionCount The number of price observations to get. * @return settlements An array of type `Settlement`, where each Settlement includes a timestamp, - * the Noun ID of that auction, the winning bid amount, and the winner's addreess. + * the Noun ID of that auction, the winning bid amount, and the winner's address. */ - function prices(uint256 auctionCount) external view returns (Settlement[] memory settlements) { + function getSettlements(uint256 auctionCount) external view returns (Settlement[] memory settlements) { uint256 latestNounId = auctionStorage.nounId; if (!auctionStorage.settled && latestNounId > 0) { latestNounId -= 1; @@ -358,17 +368,18 @@ contract NounsAuctionHouseV2 is settlements = new Settlement[](auctionCount); uint256 actualCount = 0; while (actualCount < auctionCount && latestNounId > 0) { + SettlementState memory settlementState = settlementHistory[latestNounId]; // Skip Nouner reward Nouns, they have no price // Also skips IDs with no price data - if (settlementHistory[latestNounId].winner == address(0)) { + if (settlementState.winner == address(0)) { --latestNounId; continue; } settlements[actualCount] = Settlement({ - blockTimestamp: settlementHistory[latestNounId].blockTimestamp, - amount: uint64PriceToUint256(settlementHistory[latestNounId].amount), - winner: settlementHistory[latestNounId].winner, + blockTimestamp: settlementState.blockTimestamp, + amount: uint64PriceToUint256(settlementState.amount), + winner: settlementState.winner, nounId: latestNounId }); ++actualCount; @@ -384,27 +395,66 @@ contract NounsAuctionHouseV2 is } /** - * @notice Get a range of past auction prices. - * @dev Returns prices in chronological order, as opposed to `prices(count)` which returns prices in reverse order. + * @notice Get past auction prices. + * @dev Returns prices in reverse order, meaning prices[0] will be the most recent auction price. + * @param auctionCount The number of price observations to get. + * @return prices An array of uint256 prices. + */ + function getPrices(uint256 auctionCount) external view returns (uint256[] memory prices) { + uint256 latestNounId = auctionStorage.nounId; + if (!auctionStorage.settled && latestNounId > 0) { + latestNounId -= 1; + } + + prices = new uint256[](auctionCount); + uint256 actualCount = 0; + while (actualCount < auctionCount && latestNounId > 0) { + SettlementState memory settlementState = settlementHistory[latestNounId]; + // Skip Nouner reward Nouns, they have no price + // Also skips IDs with no price data + if (settlementState.winner == address(0)) { + --latestNounId; + continue; + } + + prices[actualCount] = uint64PriceToUint256(settlementState.amount); + ++actualCount; + --latestNounId; + } + + if (auctionCount > actualCount) { + // this assembly trims the observations array, getting rid of unused cells + assembly { + mstore(prices, actualCount) + } + } + } + + /** + * @notice Get a range of past auction settlements. + * @dev Returns prices in chronological order, as opposed to `getSettlements(count)` which returns prices in reverse order. * @param startId the first Noun ID to get prices for. * @param endId end Noun ID (up to, but not including). + * @return settlements An array of type `Settlement`, where each Settlement includes a timestamp, + * the Noun ID of that auction, the winning bid amount, and the winner's address. */ - function prices(uint256 startId, uint256 endId) external view returns (Settlement[] memory settlements) { + function getSettlements(uint256 startId, uint256 endId) external view returns (Settlement[] memory settlements) { settlements = new Settlement[](endId - startId); uint256 actualCount = 0; uint256 currentId = startId; while (currentId < endId) { + SettlementState memory settlementState = settlementHistory[currentId]; // Skip Nouner reward Nouns, they have no price // Also skips IDs with no price data - if (settlementHistory[currentId].winner == address(0)) { + if (settlementState.winner == address(0)) { ++currentId; continue; } settlements[actualCount] = Settlement({ - blockTimestamp: settlementHistory[currentId].blockTimestamp, - amount: uint64PriceToUint256(settlementHistory[currentId].amount), - winner: settlementHistory[currentId].winner, + blockTimestamp: settlementState.blockTimestamp, + amount: uint64PriceToUint256(settlementState.amount), + winner: settlementState.winner, nounId: currentId }); ++actualCount; @@ -419,6 +469,39 @@ contract NounsAuctionHouseV2 is } } + /** + * @notice Get a range of past auction prices. + * @dev Returns prices in chronological order, as opposed to `getPrices(count)` which returns prices in reverse order. + * @param startId the first Noun ID to get prices for. + * @param endId end Noun ID (up to, but not including). + * @return prices An array of uint256 prices. + */ + function getPrices(uint256 startId, uint256 endId) external view returns (uint256[] memory prices) { + prices = new uint256[](endId - startId); + uint256 actualCount = 0; + uint256 currentId = startId; + while (currentId < endId) { + SettlementState memory settlementState = settlementHistory[currentId]; + // Skip Nouner reward Nouns, they have no price + // Also skips IDs with no price data + if (settlementState.winner == address(0)) { + ++currentId; + continue; + } + + prices[actualCount] = uint64PriceToUint256(settlementState.amount); + ++actualCount; + ++currentId; + } + + if (prices.length > actualCount) { + // this assembly trims the observations array, getting rid of unused cells + assembly { + mstore(prices, actualCount) + } + } + } + /** * @dev Convert an ETH price of 256 bits with 18 decimals, to 64 bits with 10 decimals. * Max supported value is 1844674407.3709551615 ETH. diff --git a/packages/nouns-contracts/contracts/interfaces/INounsAuctionHouseV2.sol b/packages/nouns-contracts/contracts/interfaces/INounsAuctionHouseV2.sol index bf64fa12b9..d043f3e823 100644 --- a/packages/nouns-contracts/contracts/interfaces/INounsAuctionHouseV2.sol +++ b/packages/nouns-contracts/contracts/interfaces/INounsAuctionHouseV2.sol @@ -87,7 +87,13 @@ interface INounsAuctionHouseV2 { function auction() external view returns (AuctionV2 memory); - function prices(uint256 auctionCount) external view returns (Settlement[] memory settlements); + function getSettlements(uint256 auctionCount) external view returns (Settlement[] memory settlements); + + function getPrices(uint256 auctionCount) external view returns (uint256[] memory prices); + + function getSettlements(uint256 startId, uint256 endId) external view returns (Settlement[] memory settlements); + + function getPrices(uint256 startId, uint256 endId) external view returns (uint256[] memory prices); function warmUpSettlementState(uint256[] calldata nounIds) external; } diff --git a/packages/nouns-contracts/test/foundry/NounsAuctionHouseGasSnapshot.t.sol b/packages/nouns-contracts/test/foundry/NounsAuctionHouseGasSnapshot.t.sol index 6d6eec7210..99027eb12c 100644 --- a/packages/nouns-contracts/test/foundry/NounsAuctionHouseGasSnapshot.t.sol +++ b/packages/nouns-contracts/test/foundry/NounsAuctionHouseGasSnapshot.t.sol @@ -9,7 +9,7 @@ import { AuctionHouseUpgrader } from './helpers/AuctionHouseUpgrader.sol'; import { NounsAuctionHouseProxy } from '../../contracts/proxies/NounsAuctionHouseProxy.sol'; import { NounsAuctionHouseProxyAdmin } from '../../contracts/proxies/NounsAuctionHouseProxyAdmin.sol'; -contract NounsAuctionHouse_GasSnapshot_createBid is DeployUtils { +abstract contract NounsAuctionHouseBaseTest is DeployUtils { INounsAuctionHouse auctionHouse; INounsToken nouns; address noundersDAO = makeAddr('noundersDAO'); @@ -31,7 +31,9 @@ contract NounsAuctionHouse_GasSnapshot_createBid is DeployUtils { vm.prank(owner); auctionHouse.unpause(); } +} +contract NounsAuctionHouse_GasSnapshot is NounsAuctionHouseBaseTest { function test_createOneBid() public { auctionHouse.createBid{ value: 1 ether }(1); } @@ -48,17 +50,63 @@ contract NounsAuctionHouse_GasSnapshot_createBid is DeployUtils { } } -contract NounsAuctionHouseV2_GasSnapshot_createBid is NounsAuctionHouse_GasSnapshot_createBid { +contract NounsAuctionHouseV2_GasSnapshot is NounsAuctionHouse_GasSnapshot { function setUp() public virtual override { super.setUp(); AuctionHouseUpgrader.upgradeAuctionHouse(owner, proxyAdmin, auctionHouseProxy); } } -contract NounsAuctionHouseV2WarmedUp_GasSnapshot_createBid is NounsAuctionHouseV2_GasSnapshot_createBid { +contract NounsAuctionHouseV2WarmedUp_GasSnapshot is NounsAuctionHouseV2_GasSnapshot { function setUp() public override { super.setUp(); nounIds = [1, 2, 3]; INounsAuctionHouseV2(address(auctionHouse)).warmUpSettlementState(nounIds); } } + +contract NounsAuctionHouseV2_HistoricPrices_GasSnapshot is NounsAuctionHouseBaseTest { + INounsAuctionHouseV2 auctionHouseV2; + + function setUp() public virtual override { + super.setUp(); + AuctionHouseUpgrader.upgradeAuctionHouse(owner, proxyAdmin, auctionHouseProxy); + auctionHouseV2 = INounsAuctionHouseV2(address(auctionHouse)); + + for (uint256 i = 1; i <= 200; ++i) { + address bidder = makeAddr(vm.toString(i)); + bidAndWinCurrentAuction(bidder, i * 1e18); + } + } + + function bidAndWinCurrentAuction(address bidder, uint256 bid) internal returns (uint256) { + uint128 nounId = auctionHouseV2.auction().nounId; + uint40 endTime = auctionHouseV2.auction().endTime; + vm.deal(bidder, bid); + vm.prank(bidder); + auctionHouseV2.createBid{ value: bid }(nounId); + vm.warp(endTime); + auctionHouseV2.settleCurrentAndCreateNewAuction(); + return block.timestamp; + } + + function test_getSettlements_90() public { + INounsAuctionHouseV2.Settlement[] memory prices = auctionHouseV2.getSettlements(90); + assertEq(prices.length, 90); + } + + function test_getPrices_90() public { + uint256[] memory prices = auctionHouseV2.getPrices(90); + assertEq(prices.length, 90); + } + + function test_getSettlements_range_90() public { + INounsAuctionHouseV2.Settlement[] memory prices = auctionHouseV2.getSettlements(1, 100); + assertEq(prices.length, 90); + } + + function test_getPrices_range_90() public { + uint256[] memory prices = auctionHouseV2.getPrices(1, 100); + assertEq(prices.length, 90); + } +} diff --git a/packages/nouns-contracts/test/foundry/NounsAuctionHouseV2.t.sol b/packages/nouns-contracts/test/foundry/NounsAuctionHouseV2.t.sol index 1f0f6bb1d9..3635419162 100644 --- a/packages/nouns-contracts/test/foundry/NounsAuctionHouseV2.t.sol +++ b/packages/nouns-contracts/test/foundry/NounsAuctionHouseV2.t.sol @@ -284,17 +284,23 @@ contract NounsAuctionHouseV2Test is NounsAuctionHouseV2TestBase { } contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { + uint256[] expectedPrices; + function test_prices_oneAuction_higherAuctionCountReturnsTheOneAuction() public { address bidder = address(0x4444); bidAndWinCurrentAuction(bidder, 1 ether); - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(2); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(2); + + assertEq(settlements.length, 1); + assertEq(settlements[0].blockTimestamp, uint32(block.timestamp)); + assertEq(settlements[0].nounId, 1); + assertEq(settlements[0].amount, 1 ether); + assertEq(settlements[0].winner, bidder); + uint256[] memory prices = auction.getPrices(2); assertEq(prices.length, 1); - assertEq(prices[0].blockTimestamp, uint32(block.timestamp)); - assertEq(prices[0].nounId, 1); - assertEq(prices[0].amount, 1 ether); - assertEq(prices[0].winner, bidder); + assertEq(prices[0], 1 ether); } function test_prices_preserves10DecimalsUnderUint64MaxValue() public { @@ -302,23 +308,29 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { // at 10 decimal points it's 1844674407.3709551615 bidAndWinCurrentAuction(makeAddr('bidder'), 1844674407.3709551615999999 ether); - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(1); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(1); - assertEq(prices.length, 1); - assertEq(prices[0].nounId, 1); - assertEq(prices[0].amount, 1844674407.3709551615 ether); - assertEq(prices[0].winner, makeAddr('bidder')); + assertEq(settlements.length, 1); + assertEq(settlements[0].nounId, 1); + assertEq(settlements[0].amount, 1844674407.3709551615 ether); + assertEq(settlements[0].winner, makeAddr('bidder')); + + uint256[] memory prices = auction.getPrices(1); + assertEq(prices[0], 1844674407.3709551615 ether); } function test_prices_overflowsGracefullyOverUint64MaxValue() public { bidAndWinCurrentAuction(makeAddr('bidder'), 1844674407.3709551617 ether); - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(1); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(1); - assertEq(prices.length, 1); - assertEq(prices[0].nounId, 1); - assertEq(prices[0].amount, 1 * 1e8); - assertEq(prices[0].winner, makeAddr('bidder')); + assertEq(settlements.length, 1); + assertEq(settlements[0].nounId, 1); + assertEq(settlements[0].amount, 1 * 1e8); + assertEq(settlements[0].winner, makeAddr('bidder')); + + uint256[] memory prices = auction.getPrices(1); + assertEq(prices[0], 1 * 1e8); } function test_prices_20Auctions_skipsNounerNounsAsExpected() public { @@ -327,20 +339,26 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { bidAndWinCurrentAuction(bidder, i * 1e18); } - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(20); - assertEq(prices[0].nounId, 22); - assertEq(prices[1].nounId, 21); - assertEq(prices[2].nounId, 19); - assertEq(prices[10].nounId, 11); - assertEq(prices[11].nounId, 9); - assertEq(prices[19].nounId, 1); - - assertEq(prices[0].amount, 20 ether); - assertEq(prices[1].amount, 19 ether); - assertEq(prices[2].amount, 18 ether); - assertEq(prices[10].amount, 10 ether); - assertEq(prices[11].amount, 9 ether); - assertEq(prices[19].amount, 1 ether); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(20); + assertEq(settlements[0].nounId, 22); + assertEq(settlements[1].nounId, 21); + assertEq(settlements[2].nounId, 19); + assertEq(settlements[10].nounId, 11); + assertEq(settlements[11].nounId, 9); + assertEq(settlements[19].nounId, 1); + + assertEq(settlements[0].amount, 20 ether); + assertEq(settlements[1].amount, 19 ether); + assertEq(settlements[2].amount, 18 ether); + assertEq(settlements[10].amount, 10 ether); + assertEq(settlements[11].amount, 9 ether); + assertEq(settlements[19].amount, 1 ether); + + uint256[] memory prices = auction.getPrices(20); + // prettier-ignore + expectedPrices = [20e18, 19e18, 18e18, 17e18, 16e18, 15e18, 14e18, 13e18, 12e18, 11e18, + 10e18, 9e18, 8e18, 7e18, 6e18, 5e18, 4e18, 3e18, 2e18, 1e18]; + assertEq(prices, expectedPrices); } function test_prices_skipsEmptySettlementsPostWarmUp() public { @@ -352,15 +370,24 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { bidAndWinCurrentAuction(bidder, i * 1e18); } - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(20); - assertEq(prices[0].nounId, 22); - assertEq(prices[1].nounId, 21); - assertEq(prices[2].nounId, 19); - assertEq(prices[10].nounId, 11); - assertEq(prices[11].nounId, 9); - assertEq(prices[19].nounId, 1); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(20); + assertEq(settlements[0].nounId, 22); + assertEq(settlements[1].nounId, 21); + assertEq(settlements[2].nounId, 19); + assertEq(settlements[10].nounId, 11); + assertEq(settlements[11].nounId, 9); + assertEq(settlements[19].nounId, 1); + + uint256[] memory prices = auction.getPrices(20); + // prettier-ignore + expectedPrices = [20e18, 19e18, 18e18, 17e18, 16e18, 15e18, 14e18, 13e18, 12e18, 11e18, + 10e18, 9e18, 8e18, 7e18, 6e18, 5e18, 4e18, 3e18, 2e18, 1e18]; + assertEq(prices, expectedPrices); + + settlements = auction.getSettlements(20, 21); + assertEq(settlements.length, 0); - prices = auction.prices(20, 21); + prices = auction.getPrices(20, 21); assertEq(prices.length, 0); } @@ -372,17 +399,21 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { auction.pause(); auction.settleAuction(); - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(2); - - assertEq(prices.length, 2); - assertEq(prices[0].blockTimestamp, uint32(bid2Timestamp)); - assertEq(prices[0].nounId, 2); - assertEq(prices[0].amount, 2 ether); - assertEq(prices[0].winner, makeAddr('bidder 2')); - assertEq(prices[1].blockTimestamp, uint32(bid1Timestamp)); - assertEq(prices[1].nounId, 1); - assertEq(prices[1].amount, 1 ether); - assertEq(prices[1].winner, makeAddr('bidder')); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(2); + + assertEq(settlements.length, 2); + assertEq(settlements[0].blockTimestamp, uint32(bid2Timestamp)); + assertEq(settlements[0].nounId, 2); + assertEq(settlements[0].amount, 2 ether); + assertEq(settlements[0].winner, makeAddr('bidder 2')); + assertEq(settlements[1].blockTimestamp, uint32(bid1Timestamp)); + assertEq(settlements[1].nounId, 1); + assertEq(settlements[1].amount, 1 ether); + assertEq(settlements[1].winner, makeAddr('bidder')); + + uint256[] memory prices = auction.getPrices(2); + assertEq(prices[0], 2 ether); + assertEq(prices[1], 1 ether); } function test_prices_givenMissingAuctionData_skipsMissingNounIDs() public { @@ -398,17 +429,23 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { bidAndWinCurrentAuction(bidder, 2 ether); bidAndWinCurrentAuction(bidder, 3 ether); - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(3); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(3); + assertEq(settlements.length, 3); + assertEq(settlements[0].nounId, 6); + assertEq(settlements[0].amount, 3 ether); + assertEq(settlements[0].winner, bidder); + assertEq(settlements[1].nounId, 2); + assertEq(settlements[1].amount, 2 ether); + assertEq(settlements[1].winner, bidder); + assertEq(settlements[2].nounId, 1); + assertEq(settlements[2].amount, 1 ether); + assertEq(settlements[2].winner, bidder); + + uint256[] memory prices = auction.getPrices(3); assertEq(prices.length, 3); - assertEq(prices[0].nounId, 6); - assertEq(prices[0].amount, 3 ether); - assertEq(prices[0].winner, bidder); - assertEq(prices[1].nounId, 2); - assertEq(prices[1].amount, 2 ether); - assertEq(prices[1].winner, bidder); - assertEq(prices[2].nounId, 1); - assertEq(prices[2].amount, 1 ether); - assertEq(prices[2].winner, bidder); + assertEq(prices[0], 3 ether); + assertEq(prices[1], 2 ether); + assertEq(prices[2], 1 ether); } function test_prices_withRange_givenBiggerRangeThanAuctionsReturnsAuctionsAndZeroObservations() public { @@ -418,19 +455,23 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { lastBidTime = bidAndWinCurrentAuction(bidder, i * 1e18); } - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(0, 5); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(0, 5); // lastest ID 4 has no settlement data, so it's not included in the result - assertEq(prices.length, 3); - assertEq(prices[0].nounId, 1); - assertEq(prices[0].amount, 1 ether); - assertEq(prices[0].winner, makeAddr('1')); - assertEq(prices[1].nounId, 2); - assertEq(prices[1].amount, 2 ether); - assertEq(prices[1].winner, makeAddr('2')); - assertEq(prices[2].blockTimestamp, uint32(lastBidTime)); - assertEq(prices[2].nounId, 3); - assertEq(prices[2].amount, 3 ether); - assertEq(prices[2].winner, makeAddr('3')); + assertEq(settlements.length, 3); + assertEq(settlements[0].nounId, 1); + assertEq(settlements[0].amount, 1 ether); + assertEq(settlements[0].winner, makeAddr('1')); + assertEq(settlements[1].nounId, 2); + assertEq(settlements[1].amount, 2 ether); + assertEq(settlements[1].winner, makeAddr('2')); + assertEq(settlements[2].blockTimestamp, uint32(lastBidTime)); + assertEq(settlements[2].nounId, 3); + assertEq(settlements[2].amount, 3 ether); + assertEq(settlements[2].winner, makeAddr('3')); + + uint256[] memory prices = auction.getPrices(0, 5); + expectedPrices = [1 ether, 2 ether, 3 ether]; + assertEq(prices, expectedPrices); } function test_prices_withRange_givenSmallerRangeThanAuctionsReturnsAuctions() public { @@ -439,20 +480,24 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { bidAndWinCurrentAuction(bidder, i * 1e18); } - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(7, 12); - assertEq(prices.length, 4); - assertEq(prices[0].nounId, 7); - assertEq(prices[0].amount, 7 ether); - assertEq(prices[0].winner, makeAddr('7')); - assertEq(prices[1].nounId, 8); - assertEq(prices[1].amount, 8 ether); - assertEq(prices[1].winner, makeAddr('8')); - assertEq(prices[2].nounId, 9); - assertEq(prices[2].amount, 9 ether); - assertEq(prices[2].winner, makeAddr('9')); - assertEq(prices[3].nounId, 11); - assertEq(prices[3].amount, 10 ether); - assertEq(prices[3].winner, makeAddr('10')); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(7, 12); + assertEq(settlements.length, 4); + assertEq(settlements[0].nounId, 7); + assertEq(settlements[0].amount, 7 ether); + assertEq(settlements[0].winner, makeAddr('7')); + assertEq(settlements[1].nounId, 8); + assertEq(settlements[1].amount, 8 ether); + assertEq(settlements[1].winner, makeAddr('8')); + assertEq(settlements[2].nounId, 9); + assertEq(settlements[2].amount, 9 ether); + assertEq(settlements[2].winner, makeAddr('9')); + assertEq(settlements[3].nounId, 11); + assertEq(settlements[3].amount, 10 ether); + assertEq(settlements[3].winner, makeAddr('10')); + + uint256[] memory prices = auction.getPrices(7, 12); + expectedPrices = [7 ether, 8 ether, 9 ether, 10 ether]; + assertEq(prices, expectedPrices); } function test_prices_withRange_givenMissingAuctionData_skipsMissingNounIDs() public { @@ -468,17 +513,21 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { bidAndWinCurrentAuction(bidder, 2 ether); bidAndWinCurrentAuction(bidder, 3 ether); - INounsAuctionHouseV2.Settlement[] memory prices = auction.prices(1, 7); - assertEq(prices.length, 3); - assertEq(prices[0].nounId, 1); - assertEq(prices[0].amount, 1 ether); - assertEq(prices[0].winner, bidder); - assertEq(prices[1].nounId, 2); - assertEq(prices[1].amount, 2 ether); - assertEq(prices[1].winner, bidder); - assertEq(prices[2].nounId, 6); - assertEq(prices[2].amount, 3 ether); - assertEq(prices[2].winner, bidder); + INounsAuctionHouseV2.Settlement[] memory settlements = auction.getSettlements(1, 7); + assertEq(settlements.length, 3); + assertEq(settlements[0].nounId, 1); + assertEq(settlements[0].amount, 1 ether); + assertEq(settlements[0].winner, bidder); + assertEq(settlements[1].nounId, 2); + assertEq(settlements[1].amount, 2 ether); + assertEq(settlements[1].winner, bidder); + assertEq(settlements[2].nounId, 6); + assertEq(settlements[2].amount, 3 ether); + assertEq(settlements[2].winner, bidder); + + uint256[] memory prices = auction.getPrices(1, 7); + expectedPrices = [1 ether, 2 ether, 3 ether]; + assertEq(prices, expectedPrices); } function test_setPrices_revertsForNonOwner() public { @@ -527,13 +576,16 @@ contract NounsAuctionHouseV2_OracleTest is NounsAuctionHouseV2TestBase { vm.prank(auction.owner()); auction.setPrices(settlements); - INounsAuctionHouseV2.Settlement[] memory actualSettlements = auction.prices(0, 23); + INounsAuctionHouseV2.Settlement[] memory actualSettlements = auction.getSettlements(0, 23); + uint256[] memory actualPrices = auction.getPrices(0, 23); assertEq(actualSettlements.length, 20); + assertEq(actualPrices.length, 20); for (uint256 i = 0; i < 20; ++i) { assertEq(settlements[i].blockTimestamp, actualSettlements[i].blockTimestamp); assertEq(settlements[i].amount, actualSettlements[i].amount); assertEq(settlements[i].winner, actualSettlements[i].winner); assertEq(settlements[i].nounId, actualSettlements[i].nounId); + assertEq(settlements[i].amount, actualPrices[i]); } } }