Skip to content

Commit

Permalink
feat: settle
Browse files Browse the repository at this point in the history
  • Loading branch information
Schlagonia committed Oct 25, 2024
1 parent adf804e commit eee8a82
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 100 deletions.
105 changes: 70 additions & 35 deletions src/Auctions/DumperAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {

/// @notice Store all the auction specific information.
struct AuctionInfo {
uint128 kicked;
uint128 scaler;
uint64 kicked;
uint64 scaler;
uint128 initialAvailable;
uint128 currentAvailable;
}

uint256 internal constant WAD = 1e18;
Expand Down Expand Up @@ -129,17 +128,39 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
return wantInfo.tokenAddress;
}

function available(address _from) external view virtual returns (uint256) {
if (auctions[_from].kicked + auctionLength > block.timestamp) {
return auctions[_from].currentAvailable;
}
return 0;
/**
* @notice Get the available amount for the auction.
* @param _from The address of the token to be auctioned.
* @return . The available amount for the auction.
*/
function available(address _from) public view virtual returns (uint256) {
if (!isActive(_from)) return 0;

return
Maths.min(
auctions[_from].initialAvailable,
ERC20(_from).balanceOf(address(this))
);
}

/**
* @notice Get the kicked timestamp for the auction.
* @param _from The address of the token to be auctioned.
* @return . The kicked timestamp for the auction.
*/
function kicked(address _from) external view virtual returns (uint256) {
return auctions[_from].kicked;
}

/**
* @notice Check if the auction is active.
* @param _from The address of the token to be auctioned.
* @return . Whether the auction is active.
*/
function isActive(address _from) public view virtual returns (bool) {
return auctions[_from].kicked + auctionLength > block.timestamp;
}

/**
* @notice Get all the enabled auctions.
*/
Expand All @@ -160,14 +181,28 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
*/
function kickable(address _from) external view virtual returns (uint256) {
// If not enough time has passed then `kickable` is 0.
if (auctions[_from].kicked + auctionLength > block.timestamp) {
return 0;
}
if (isActive(_from)) return 0;

// Use the full balance of this contract.
return ERC20(_from).balanceOf(address(this));
}

/**
* @notice Gets the amount of `want` needed to buy the available amount of `from`.
* @param _from The address of the token to be auctioned.
* @return . The amount of `want` needed to fulfill the take amount.
*/
function getAmountNeeded(
address _from
) external view virtual returns (uint256) {
return
_getAmountNeeded(
auctions[_from],
available(_from),
block.timestamp
);
}

/**
* @notice Gets the amount of `want` needed to buy a specific amount of `from`.
* @param _from The address of the token to be auctioned.
Expand Down Expand Up @@ -205,10 +240,6 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
uint256 _amountToTake,
uint256 _timestamp
) internal view virtual returns (uint256) {
_amountToTake = _amountToTake > _auction.currentAvailable
? _auction.currentAvailable
: _amountToTake;

return
// Scale _amountToTake to 1e18
(_amountToTake *
Expand Down Expand Up @@ -301,7 +332,7 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
require(decimals <= 18, "unsupported decimals");

// Store all needed info.
auctions[_from].scaler = uint128(WAD / 10 ** decimals);
auctions[_from].scaler = uint64(WAD / 10 ** decimals);

ERC20(_from).safeApprove(VAULT_RELAYER, type(uint256).max);

Expand Down Expand Up @@ -413,9 +444,8 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
require(_available != 0, "nothing to kick");

// Update the auctions status.
auctions[_from].kicked = uint128(block.timestamp);
auctions[_from].kicked = uint64(block.timestamp);
auctions[_from].initialAvailable = uint128(_available);
auctions[_from].currentAvailable = uint128(_available);

emit AuctionKicked(_from, _available);
}
Expand Down Expand Up @@ -491,9 +521,8 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
);

// Max amount that can be taken.
_amountTaken = auction.currentAvailable > _maxAmount
? _maxAmount
: auction.currentAvailable;
uint256 available = available(_from);
_amountTaken = available > _maxAmount ? _maxAmount : available;

// Get the amount needed
uint256 needed = _getAmountNeeded(
Expand All @@ -507,13 +536,6 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
// Send `from`.
ERC20(_from).safeTransfer(_receiver, _amountTaken);

// How much is left in this auction.
uint256 left;
unchecked {
left = auction.currentAvailable - _amountTaken;
}
auctions[_from].currentAvailable = uint128(left);

// If the caller has specified data.
if (_data.length != 0) {
// Do the callback.
Expand Down Expand Up @@ -557,19 +579,32 @@ contract DumperAuction is Governance2Step, ReentrancyGuard {
require(_hash == order.hash(COW_DOMAIN_SEPARATOR), "bad order");
require(paymentAmount > 0, "zero amount");
require(order.feeAmount == 0, "fee");
require(order.validTo < block.timestamp + 1 days, "expired");
require(order.validTo < auction.kicked + auctionLength, "expired");
require(order.appData == bytes32(0), "app data");
require(order.buyAmount >= paymentAmount, "bad price");
require(address(order.buyToken) == want(), "bad token");
require(order.receiver == receiver, "bad receiver");
require(order.sellAmount == auction.currentAvailable, "bad amount");
require(
order.sellToken.balanceOf(address(this)) >=
auction.currentAvailable,
"taken"
);
require(order.sellAmount <= auction.initialAvailable, "bad amount");

// If all checks pass, return the magic value
return this.isValidSignature.selector;
}

/**
* @notice Allows the auction to be stopped if the full amount is taken.
* @param _from The address of the token to be auctioned.
*/
function settle(address _from) external virtual {
require(isActive(_from), "!active");
require(ERC20(_from).balanceOf(address(this)) == 0, "!empty");

auctions[_from].kicked = uint64(0);
}

function sweep(address _token) external virtual onlyGovernance {
ERC20(_token).safeTransfer(
msg.sender,
ERC20(_token).balanceOf(address(this))
);
}
}
106 changes: 41 additions & 65 deletions src/test/DumperAuction.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,13 @@ contract DumperAuctionTest is Setup, ITaker {
assertEq(auction.price(from), 0);
assertEq(auction.receiver(), address(this));

(
uint128 _kicked,
uint128 _scaler,
uint128 _available,
uint128 _currentAvailable
) = auction.auctions(from);
(uint128 _kicked, uint128 _scaler, uint128 _initialAvailable) = auction
.auctions(from);

assertEq(_kicked, 0);
assertEq(_scaler, 1e12);
assertEq(_available, 0);
assertEq(_currentAvailable, 0);
assertEq(_initialAvailable, 0);
assertEq(auction.available(from), 0);

// Kicking it reverts
vm.expectRevert("nothing to kick");
Expand All @@ -120,17 +116,13 @@ contract DumperAuctionTest is Setup, ITaker {

assertEq(auction.getAllEnabledAuctions().length, 1);

(
uint128 _kicked,
uint128 _scaler,
uint128 _available,
uint128 _currentAvailable
) = auction.auctions(from);
(uint128 _kicked, uint128 _scaler, uint128 _initialAvailable) = auction
.auctions(from);

assertEq(_kicked, 0);
assertEq(_scaler, 1e12);
assertEq(_available, 0);
assertEq(_currentAvailable, 0);
assertEq(_initialAvailable, 0);
assertEq(auction.available(from), 0);

vm.expectRevert("!governance");
vm.prank(management);
Expand All @@ -142,14 +134,12 @@ contract DumperAuctionTest is Setup, ITaker {

assertEq(auction.getAllEnabledAuctions().length, 0);

(_kicked, _scaler, _available, _currentAvailable) = auction.auctions(
from
);
(_kicked, _scaler, _initialAvailable) = auction.auctions(from);

assertEq(_kicked, 0);
assertEq(_scaler, 0);
assertEq(_available, 0);
assertEq(_currentAvailable, 0);
assertEq(_initialAvailable, 0);
assertEq(auction.available(from), 0);
}

function test_kickAuction(uint256 _amount) public {
Expand All @@ -166,32 +156,28 @@ contract DumperAuctionTest is Setup, ITaker {
auction.enable(from);

assertEq(auction.kickable(from), 0);
(
uint128 _kicked,
uint128 _scaler,
uint128 _available,
uint128 _currentAvailable
) = auction.auctions(from);
(uint128 _kicked, uint128 _scaler, uint128 _initialAvailable) = auction
.auctions(from);

assertEq(_kicked, 0);
assertEq(_scaler, 1e10);
assertEq(_available, 0);
assertEq(_currentAvailable, 0);
assertEq(_initialAvailable, 0);
assertEq(auction.available(from), 0);

airdrop(ERC20(from), address(auction), _amount);

assertEq(auction.kickable(from), _amount);
(_kicked, , _available, _currentAvailable) = auction.auctions(from);
(_kicked, , _initialAvailable) = auction.auctions(from);
assertEq(_kicked, 0);
assertEq(_available, 0);
assertEq(_initialAvailable, 0);
assertEq(auction.available(from), 0);

uint256 available = auction.kick(from);

assertEq(auction.kickable(from), 0);
(_kicked, , _available, _currentAvailable) = auction.auctions(from);
(_kicked, , _initialAvailable) = auction.auctions(from);
assertEq(_kicked, block.timestamp);
assertEq(_available, _amount);
assertEq(_initialAvailable, _amount);
assertEq(auction.available(from), _amount);
uint256 startingPrice = ((auction.startingPrice() *
(WAD / wantScaler)) * 1e18) /
Expand Down Expand Up @@ -234,6 +220,7 @@ contract DumperAuctionTest is Setup, ITaker {

assertEq(auction.price(from), 0);
assertEq(auction.getAmountNeeded(from, _amount), 0);
assertEq(auction.available(from), 0);

assertEq(auction.kickable(from), _amount);
}
Expand All @@ -259,16 +246,12 @@ contract DumperAuctionTest is Setup, ITaker {
uint256 available = auction.kick(from);

assertEq(auction.kickable(from), 0);
(
uint128 _kicked,
uint128 _scaler,
uint128 _available,
uint128 _currentAvailable
) = auction.auctions(from);
(uint128 _kicked, uint128 _scaler, uint128 _initialAvailable) = auction
.auctions(from);
assertEq(_kicked, block.timestamp);
assertEq(_scaler, 1e10);
assertEq(_available, _amount);
assertEq(_currentAvailable, _amount);
assertEq(_initialAvailable, _amount);
assertEq(auction.available(from), _amount);

skip(auction.auctionLength() / 2);

Expand All @@ -285,9 +268,9 @@ contract DumperAuctionTest is Setup, ITaker {

assertEq(amountTaken, _amount);

(, , _available, _currentAvailable) = auction.auctions(from);
assertEq(_available, _amount);
assertEq(_currentAvailable, 0);
(, , _initialAvailable) = auction.auctions(from);
assertEq(_initialAvailable, _amount);
assertEq(auction.available(from), 0);

assertEq(ERC20(asset).balanceOf(address(this)), beforeAsset);
assertEq(ERC20(from).balanceOf(address(this)), before + _amount);
Expand Down Expand Up @@ -318,15 +301,12 @@ contract DumperAuctionTest is Setup, ITaker {
auction.kick(from);

assertEq(auction.kickable(from), 0);
(
uint256 _kicked,
uint256 _scaler,
uint256 _available,
uint256 _currentAvailable
) = auction.auctions(from);
(uint256 _kicked, uint256 _scaler, uint256 _initialAvailable) = auction
.auctions(from);
assertEq(_kicked, block.timestamp);
assertEq(_scaler, 1e10);
assertEq(_available, _amount);
assertEq(_initialAvailable, _amount);
assertEq(auction.available(from), _amount);

skip(auction.auctionLength() / 2);

Expand All @@ -345,9 +325,9 @@ contract DumperAuctionTest is Setup, ITaker {

assertEq(amountTaken, toTake);

(, , _available, _currentAvailable) = auction.auctions(from);
assertEq(_available, _amount);
assertEq(_currentAvailable, left);
(, , _initialAvailable) = auction.auctions(from);
assertEq(_initialAvailable, _amount);
assertEq(auction.available(from), left);
assertEq(ERC20(asset).balanceOf(address(this)), beforeAsset);
assertEq(ERC20(from).balanceOf(address(this)), before + toTake);
assertEq(ERC20(from).balanceOf(address(auction)), left);
Expand Down Expand Up @@ -376,16 +356,12 @@ contract DumperAuctionTest is Setup, ITaker {
auction.kick(from);

assertEq(auction.kickable(from), 0);
(
uint256 _kicked,
uint256 _scaler,
uint256 _available,
uint256 _currentAvailable
) = auction.auctions(from);
(uint256 _kicked, uint256 _scaler, uint256 _initialAvailable) = auction
.auctions(from);
assertEq(_kicked, block.timestamp);
assertEq(_scaler, 1e10);
assertEq(_available, _amount);

assertEq(_initialAvailable, _amount);
assertEq(auction.available(from), _amount);
skip(auction.auctionLength() / 2);

uint256 toTake = _amount / 2;
Expand All @@ -409,9 +385,9 @@ contract DumperAuctionTest is Setup, ITaker {
assertTrue(callbackHit);
assertEq(amountTaken, toTake);

(, , _available, _currentAvailable) = auction.auctions(from);
assertEq(_available, _amount);
assertEq(_currentAvailable, left);
(, , _initialAvailable) = auction.auctions(from);
assertEq(_initialAvailable, _amount);
assertEq(auction.available(from), left);
assertEq(ERC20(asset).balanceOf(address(this)), beforeAsset);
assertEq(ERC20(from).balanceOf(address(this)), before + toTake);
assertEq(ERC20(from).balanceOf(address(auction)), left);
Expand Down

0 comments on commit eee8a82

Please sign in to comment.