Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StreamEscrow #866

Open
wants to merge 106 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
018a92c
initial poc code from escrowed streams
davidbrai Sep 10, 2024
a50aa50
fix bug
davidbrai Sep 10, 2024
5a7aa57
add tests
davidbrai Sep 11, 2024
66f8ca9
first auction doesn't stream yet
davidbrai Sep 11, 2024
807fad7
immediately stream remainder
davidbrai Sep 11, 2024
dc55686
cant cancel finished stream
davidbrai Sep 12, 2024
1b39ffb
forward streams only if 24 hours passed
davidbrai Sep 12, 2024
36d29dc
add auction house setter
davidbrai Sep 12, 2024
89fa882
add a cancel multiple streams convenience function
davidbrai Sep 12, 2024
b29dd94
add event
davidbrai Sep 12, 2024
c2ac8cc
rename
davidbrai Oct 8, 2024
90460ab
rename auction house from v2 to v3
davidbrai Oct 8, 2024
4c5e222
update auction house upgrade fork test
davidbrai Oct 9, 2024
cea51ca
cleanup
davidbrai Oct 9, 2024
0bd01f3
change stream permissions
davidbrai Oct 21, 2024
cc80771
stream escrows: rename auctions to ticks
davidbrai Oct 21, 2024
0e7f233
send streamed eth to treasury immediately
davidbrai Oct 22, 2024
417153f
rename var
davidbrai Oct 25, 2024
226c9a6
rename var
davidbrai Oct 25, 2024
41c33c5
cleanup
davidbrai Oct 25, 2024
7dc3fe0
cleanup
davidbrai Oct 25, 2024
1e8607c
add ethRecipient and nounsRecipient addresses settable by daoExecutor
davidbrai Oct 25, 2024
bab1657
bugfix
davidbrai Nov 4, 2024
8c7ed73
refactor finish streams
davidbrai Nov 4, 2024
657ba6a
added fastForward function
davidbrai Nov 4, 2024
c24bf96
better require statements
davidbrai Nov 5, 2024
92d1a20
rename ticks to currentTick
davidbrai Nov 5, 2024
657dd70
create stream: must be whitelisted and noun owner or approved
davidbrai Nov 6, 2024
911bf00
add setAllowedToCreateStream
davidbrai Nov 6, 2024
7601506
remove todo
davidbrai Nov 6, 2024
5b6cb53
adding tests
davidbrai Nov 6, 2024
9149e62
add tests
davidbrai Nov 6, 2024
5cbda80
add test
davidbrai Nov 6, 2024
b57a9e7
add test
davidbrai Nov 7, 2024
9670bdf
add test
davidbrai Nov 7, 2024
a175286
tests
davidbrai Nov 7, 2024
700695a
small refactor
davidbrai Nov 7, 2024
8a284f0
add events
davidbrai Nov 7, 2024
e105c22
gas optimizations
davidbrai Nov 7, 2024
565fca7
gas optimizations
davidbrai Nov 7, 2024
955cef4
natspec
davidbrai Nov 7, 2024
c69432f
add tests
davidbrai Nov 8, 2024
5dc56f0
add events
davidbrai Nov 8, 2024
0d6bda8
fix test
davidbrai Nov 8, 2024
4b1093e
natspec
davidbrai Nov 8, 2024
45b8561
natspec
davidbrai Nov 8, 2024
b866c04
minor refactor
davidbrai Nov 11, 2024
a150478
minor refactor
davidbrai Nov 11, 2024
fca5a6e
natspec
davidbrai Nov 11, 2024
9b0f753
extract minimumTickDuration
davidbrai Nov 11, 2024
d205cd7
remove stale comment
davidbrai Nov 11, 2024
e8ed97c
fix natspec
davidbrai Nov 11, 2024
a2ab0a9
cleanup
davidbrai Nov 11, 2024
9a301cd
minor
davidbrai Nov 11, 2024
7899ded
natspec
davidbrai Nov 11, 2024
04c70a2
cleanup
davidbrai Nov 11, 2024
cd0f572
cleanup
davidbrai Nov 11, 2024
bb71819
add test
davidbrai Nov 11, 2024
7689e3b
minor rename
davidbrai Nov 11, 2024
e059817
add fork test
davidbrai Nov 11, 2024
37e3e44
add more fork tests
davidbrai Nov 11, 2024
a759003
natspec
davidbrai Nov 12, 2024
d82688b
add rescueTokens function
davidbrai Nov 12, 2024
bff9090
Merge remote-tracking branch 'origin/master' into verbs-stream-escrow…
davidbrai Nov 12, 2024
e385d78
modify stream escrow events
davidbrai Nov 14, 2024
bd2278d
add deploy scripts for stream escrow
davidbrai Nov 14, 2024
a87718e
hardhat script deploy with auction house v2
davidbrai Nov 14, 2024
d30f27b
improve event
davidbrai Nov 14, 2024
291f12e
subgraph: streamescrow events wiring
eladmallel Nov 18, 2024
6a9f602
subgraph: handling some streams events WIP
eladmallel Nov 18, 2024
17aea76
subgraph: stream escrow first tests WIP
eladmallel Nov 18, 2024
a46154c
subgraph: more tests WIP
eladmallel Nov 18, 2024
f4e472b
subgraph: support multiple streams per noun
eladmallel Nov 19, 2024
3634f28
subgraph: add stream escrow tests
eladmallel Nov 19, 2024
fca48d6
stream escrow: add events in constructor
eladmallel Nov 19, 2024
a3b8be2
ci fix attemp
davidbrai Nov 20, 2024
71ed6ce
stream escrow: improve events
davidbrai Nov 20, 2024
1fb7d84
add fastForwardMultipleStreams
davidbrai Nov 20, 2024
ed6dc7d
Revert "add fastForwardMultipleStreams"
davidbrai Nov 20, 2024
f2ee6d0
Merge branch 'verbs-stream-escrow-nouner-can-create-stream' into verb…
davidbrai Nov 20, 2024
79bb07f
add fastForwardMultipleStreams
davidbrai Nov 20, 2024
47394bd
subgraph: update stream escrow ABI and set stream start tick
eladmallel Nov 20, 2024
8395db0
subgraph: fix bug
eladmallel Nov 20, 2024
cf241d1
subgraph: update ethStreamedPerTick on more events
eladmallel Nov 20, 2024
7949033
Merge pull request #869 from nounsDAO/verbs-stream-escrow-subgraph
davidbrai Nov 21, 2024
eb6194b
subgraph: new sepolia config for stream escrow
eladmallel Nov 21, 2024
d789cf3
subgraph: fix missing stream per tick update
eladmallel Nov 21, 2024
09d5d0d
subgraph: fix past streams saving
eladmallel Nov 22, 2024
172db75
add unstreamedETHForNoun
davidbrai Dec 2, 2024
f1c4fdf
add test
davidbrai Dec 2, 2024
a1d38e3
don't allow nounsRecipient to be zero address
davidbrai Dec 3, 2024
8888e0d
add natspec
davidbrai Dec 3, 2024
eaab477
minor refactor and natspec
davidbrai Dec 3, 2024
41c3b43
Merge pull request #870 from nounsDAO/verbs-stream-escrow-unstreamed-…
davidbrai Dec 3, 2024
d9ec4b5
refactor to follow checks-effects-interactions
davidbrai Dec 4, 2024
4285edb
Merge remote-tracking branch 'origin/verbs-stream-escrow-fix-audit-is…
davidbrai Dec 11, 2024
8df901f
use safeTransfer when rescuing tokens
davidbrai Dec 11, 2024
adede58
Merge pull request #873 from nounsDAO/verbs-stream-escrow-audit-fixes
davidbrai Dec 12, 2024
90a5dcd
pin subgraph ci on ubuntu 22
davidbrai Dec 12, 2024
55e3892
add subgraph ci file to trigger ci
davidbrai Dec 12, 2024
eee11e5
Merge pull request #874 from nounsDAO/verbs-stream-escrow-fix-subgrap…
davidbrai Dec 12, 2024
aa116ec
add view function for ticksLeft in stream
davidbrai Dec 12, 2024
00edb18
cleanup
davidbrai Dec 12, 2024
308b52d
add sherlock audit for stream escrow
davidbrai Dec 12, 2024
575b15b
add deployment info
davidbrai Dec 12, 2024
c46406b
Merge pull request #875 from nounsDAO/verbs-stream-escrow-add-ticks-left
davidbrai Dec 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 30 additions & 26 deletions packages/nouns-contracts/contracts/NounsAuctionHouseV3.sol
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ import { PausableUpgradeable } from '@openzeppelin/contracts-upgradeable/securit
import { ReentrancyGuardUpgradeable } from '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol';
import { OwnableUpgradeable } from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { INounsAuctionHouseV2 } from './interfaces/INounsAuctionHouseV2.sol';
import { INounsAuctionHouseV3 } from './interfaces/INounsAuctionHouseV3.sol';
import { IStreamEscrow } from './interfaces/IStreamEscrow.sol';
import { INounsToken } from './interfaces/INounsToken.sol';
import { IWETH } from './interfaces/IWETH.sol';
@@ -38,7 +38,7 @@ import { IWETH } from './interfaces/IWETH.sol';
* storage layout as the NounsAuctionHouse contract
*/
contract NounsAuctionHouseV3 is
INounsAuctionHouseV2,
INounsAuctionHouseV3,
PausableUpgradeable,
ReentrancyGuardUpgradeable,
OwnableUpgradeable
@@ -65,7 +65,7 @@ contract NounsAuctionHouseV3 is
uint8 public minBidIncrementPercentage;

/// @notice The active auction
INounsAuctionHouseV2.AuctionV2 public auctionStorage;
INounsAuctionHouseV3.AuctionV2 public auctionStorage;

/// @notice The Nouns price feed state
mapping(uint256 => SettlementState) settlementHistory;
@@ -109,27 +109,6 @@ contract NounsAuctionHouseV3 is
streamEscrow = IStreamEscrow(_streamEscrow);
}

function setStreamEscrowParams(
uint16 _immediateTreasuryBPs,
uint16 _streamLengthInTicks,
address _streamEscrow
) external onlyOwner {
require(_immediateTreasuryBPs <= 10_000, 'immediateTreasuryBPs too high');
immediateTreasuryBPs = _immediateTreasuryBPs;
streamLengthInTicks = _streamLengthInTicks;
streamEscrow = IStreamEscrow(_streamEscrow);
}

function setImmediateTreasuryBPs(uint16 _immediateTreasuryBPs) external onlyOwner {
require(_immediateTreasuryBPs <= 10_000, 'immediateTreasuryBPs too high');
immediateTreasuryBPs = _immediateTreasuryBPs;
}

function setStreamLengthInTicks(uint16 _streamLengthInTicks) external onlyOwner {
require(_streamLengthInTicks > 0, 'streamLengthInTicks too low');
streamLengthInTicks = _streamLengthInTicks;
}

/**
* @notice Settle the current auction, mint a new Noun, and put it up for auction.
*/
@@ -161,7 +140,7 @@ contract NounsAuctionHouseV3 is
* @dev This contract only accepts payment in ETH.
*/
function createBid(uint256 nounId, uint32 clientId) public payable override {
INounsAuctionHouseV2.AuctionV2 memory _auction = auctionStorage;
INounsAuctionHouseV3.AuctionV2 memory _auction = auctionStorage;

(uint192 _reservePrice, uint56 _timeBuffer, uint8 _minBidIncrementPercentage) = (
reservePrice,
@@ -272,6 +251,31 @@ contract NounsAuctionHouseV3 is
emit AuctionMinBidIncrementPercentageUpdated(_minBidIncrementPercentage);
}

function setStreamEscrowParams(
eladmallel marked this conversation as resolved.
Show resolved Hide resolved
uint16 _immediateTreasuryBPs,
uint16 _streamLengthInTicks,
address _streamEscrow
) external onlyOwner {
require(_immediateTreasuryBPs <= 10_000, 'immediateTreasuryBPs too high');
immediateTreasuryBPs = _immediateTreasuryBPs;
streamLengthInTicks = _streamLengthInTicks;
streamEscrow = IStreamEscrow(_streamEscrow);

emit StreamEscrowParamsUpdated(_immediateTreasuryBPs, _streamLengthInTicks, _streamEscrow);
}

function setImmediateTreasuryBPs(uint16 _immediateTreasuryBPs) external onlyOwner {
require(_immediateTreasuryBPs <= 10_000, 'immediateTreasuryBPs too high');
immediateTreasuryBPs = _immediateTreasuryBPs;
emit ImmediateTreasuryBPsUpdated(_immediateTreasuryBPs);
}

function setStreamLengthInTicks(uint16 _streamLengthInTicks) external onlyOwner {
require(_streamLengthInTicks > 0, 'streamLengthInTicks too low');
streamLengthInTicks = _streamLengthInTicks;
emit StreamLengthInTicksUpdated(_streamLengthInTicks);
}

/**
* @notice Create an auction.
* @dev Store the auction details in the `auction` state variable and emit an AuctionCreated event.
@@ -304,7 +308,7 @@ contract NounsAuctionHouseV3 is
* @dev If there are no bids, the Noun is burned.
*/
function _settleAuction() internal {
INounsAuctionHouseV2.AuctionV2 memory _auction = auctionStorage;
INounsAuctionHouseV3.AuctionV2 memory _auction = auctionStorage;

require(_auction.startTime != 0, "Auction hasn't begun");
require(!_auction.settled, 'Auction has already been settled');
177 changes: 177 additions & 0 deletions packages/nouns-contracts/contracts/interfaces/INounsAuctionHouseV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-3.0

/// @title Interface for Noun Auction Houses V2

/*********************************
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░█████████░░█████████░░░ *
* ░░░░░░██░░░████░░██░░░████░░░ *
* ░░██████░░░████████░░░████░░░ *
* ░░██░░██░░░████░░██░░░████░░░ *
* ░░██░░██░░░████░░██░░░████░░░ *
* ░░░░░░█████████░░█████████░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
* ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
*********************************/

pragma solidity ^0.8.19;

import { INounsToken } from './INounsToken.sol';
import { IStreamEscrow } from './IStreamEscrow.sol';

interface INounsAuctionHouseV3 {
struct AuctionV2 {
// ID for the Noun (ERC721 token ID)
uint96 nounId;
// ID of the client that facilitated the latest bid, used for client rewards
uint32 clientId;
// The current highest bid amount
uint128 amount;
// The time that the auction started
uint40 startTime;
// The time that the auction is scheduled to end
uint40 endTime;
// The address of the current highest bid
address payable bidder;
// Whether or not the auction has been settled
bool settled;
}

/// @dev We use this struct as the return value of the `auction` function, to maintain backwards compatibility.
struct AuctionV2View {
// ID for the Noun (ERC721 token ID)
uint96 nounId;
// The current highest bid amount
uint128 amount;
// The time that the auction started
uint40 startTime;
// The time that the auction is scheduled to end
uint40 endTime;
// The address of the current highest bid
address payable bidder;
// Whether or not the auction has been settled
bool settled;
}

struct SettlementState {
// The block.timestamp when the auction was settled.
uint32 blockTimestamp;
// The winning bid amount, with 10 decimal places (reducing accuracy to save bits).
uint64 amount;
// The address of the auction winner.
address winner;
// ID of the client that facilitated the winning bid, used for client rewards.
uint32 clientId;
// Used only to warm up the storage slot for clientId without setting the clientId value.
bool slotWarmedUp;
}

struct Settlement {
// The block.timestamp when the auction was settled.
uint32 blockTimestamp;
// The winning bid amount, converted from 10 decimal places to 18, for better client UX.
uint256 amount;
// The address of the auction winner.
address winner;
// ID for the Noun (ERC721 token ID).
uint256 nounId;
// ID of the client that facilitated the winning bid, used for client rewards
uint32 clientId;
}

/// @dev Using this struct when setting historic prices, and excluding clientId to save gas.
struct SettlementNoClientId {
// The block.timestamp when the auction was settled.
uint32 blockTimestamp;
// The winning bid amount, converted from 10 decimal places to 18, for better client UX.
uint256 amount;
// The address of the auction winner.
address winner;
// ID for the Noun (ERC721 token ID).
uint256 nounId;
}

event AuctionCreated(uint256 indexed nounId, uint256 startTime, uint256 endTime);

event AuctionBid(uint256 indexed nounId, address sender, uint256 value, bool extended);

event AuctionBidWithClientId(uint256 indexed nounId, uint256 value, uint32 indexed clientId);

event AuctionExtended(uint256 indexed nounId, uint256 endTime);

event AuctionSettled(uint256 indexed nounId, address winner, uint256 amount);

event AuctionSettledWithClientId(uint256 indexed nounId, uint32 indexed clientId);

event AuctionTimeBufferUpdated(uint256 timeBuffer);

event AuctionReservePriceUpdated(uint256 reservePrice);

event AuctionMinBidIncrementPercentageUpdated(uint256 minBidIncrementPercentage);

event StreamEscrowParamsUpdated(uint16 immediateTreasuryBPs, uint16 streamLengthInTicks, address streamEscrow);

event ImmediateTreasuryBPsUpdated(uint16 immediateTreasuryBPs);

event StreamLengthInTicksUpdated(uint16 streamLengthInTicks);

function settleAuction() external;

function settleCurrentAndCreateNewAuction() external;

function createBid(uint256 nounId) external payable;

function createBid(uint256 nounId, uint32 clientId) external payable;

function pause() external;

function unpause() external;

function setTimeBuffer(uint56 timeBuffer) external;

function timeBuffer() external view returns (uint56);

function setReservePrice(uint192 reservePrice) external;

function reservePrice() external view returns (uint192);

function setMinBidIncrementPercentage(uint8 minBidIncrementPercentage) external;

function minBidIncrementPercentage() external view returns (uint8);

function nouns() external view returns (INounsToken);

function weth() external view returns (address);

function auction() external view returns (AuctionV2View memory);

function getSettlements(
uint256 auctionCount,
bool skipEmptyValues
) external view returns (Settlement[] memory settlements);

function getPrices(uint256 auctionCount) external view returns (uint256[] memory prices);

function getSettlements(
uint256 startId,
uint256 endId,
bool skipEmptyValues
) external view returns (Settlement[] memory settlements);

function getSettlementsFromIdtoTimestamp(
uint256 startId,
uint256 endTimestamp,
bool skipEmptyValues
) external view returns (Settlement[] memory settlements);

function warmUpSettlementState(uint256 startId, uint256 endId) external;

function duration() external view returns (uint256);

function biddingClient(uint256 nounId) external view returns (uint32 clientId);

function setPrices(SettlementNoClientId[] memory settlements) external;

function streamEscrow() external view returns (IStreamEscrow);
}
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import { DeployUtils } from './helpers/DeployUtils.sol';
import { NounsAuctionHouseProxy } from '../../contracts/proxies/NounsAuctionHouseProxy.sol';
import { NounsAuctionHouse } from '../../contracts/NounsAuctionHouse.sol';
import { NounsAuctionHouseV3 } from '../../contracts/NounsAuctionHouseV3.sol';
import { INounsAuctionHouseV2 as IAH } from '../../contracts/interfaces/INounsAuctionHouseV2.sol';
import { INounsAuctionHouseV3 as IAH } from '../../contracts/interfaces/INounsAuctionHouseV3.sol';
import { BidderWithGasGriefing } from './helpers/BidderWithGasGriefing.sol';

contract NounsAuctionHouseV3TestBase is Test, DeployUtils {
@@ -279,6 +279,8 @@ contract AuctionHouseStreamingTest is NounsAuctionHouseV3TestBase {
function test_treasuryPercentageIs100() public {
uint256 nounId = auction.auction().nounId;
vm.prank(owner);
vm.expectEmit();
emit IAH.ImmediateTreasuryBPsUpdated(10_000);
auction.setImmediateTreasuryBPs(10_000);

// settle an auction
@@ -999,6 +1001,8 @@ contract NounsAuctionHouseV2_OwnerFunctionsTest is NounsAuctionHouseV3TestBase {

function test_setStreamEscrowParams_worksForOWner() public {
vm.prank(IOwner(address(auction)).owner());
vm.expectEmit();
emit IAH.StreamEscrowParamsUpdated(1000, 501, address(123));
auction.setStreamEscrowParams({
_immediateTreasuryBPs: 1000,
_streamLengthInTicks: 500,
@@ -1023,6 +1027,8 @@ contract NounsAuctionHouseV2_OwnerFunctionsTest is NounsAuctionHouseV3TestBase {
assertEq(auction.streamLengthInTicks(), 1500);

vm.prank(IOwner(address(auction)).owner());
vm.expectEmit();
emit IAH.StreamLengthInTicksUpdated(1);
auction.setStreamLengthInTicks(1);

assertEq(auction.streamLengthInTicks(), 1);
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.6;

import { INounsAuctionHouseV2 } from '../../../contracts/interfaces/INounsAuctionHouseV2.sol';
import { INounsAuctionHouseV3 } from '../../../contracts/interfaces/INounsAuctionHouseV3.sol';

contract BidderWithGasGriefing {
function bid(INounsAuctionHouseV2 auctionHouse, uint256 nounId) public payable {
function bid(INounsAuctionHouseV3 auctionHouse, uint256 nounId) public payable {
auctionHouse.createBid{ value: msg.value }(nounId);
}