Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/verbs-auctionv2-infinite-oracle'…
Browse files Browse the repository at this point in the history
… into verbs-spend-or-burn-excess-eth-every-n-auctions
  • Loading branch information
davidbrai committed Oct 6, 2023
2 parents 3b1256c + 3b536b0 commit 44ef0b9
Show file tree
Hide file tree
Showing 5 changed files with 321 additions and 128 deletions.
22 changes: 13 additions & 9 deletions packages/nouns-contracts/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
121 changes: 102 additions & 19 deletions packages/nouns-contracts/contracts/NounsAuctionHouseV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -123,24 +127,30 @@ 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'
);

auctionStorage.amount = uint128(msg.value);
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);
}

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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);
}
Expand All @@ -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);
}
}
Loading

0 comments on commit 44ef0b9

Please sign in to comment.