Skip to content

Commit

Permalink
add slashing
Browse files Browse the repository at this point in the history
  • Loading branch information
0xBeans committed Oct 29, 2022
1 parent 667afdb commit a744b42
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 0 deletions.
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@openzeppelin/=lib/openzeppelin-contracts/contracts
solmate/=lib/solmate/src/
solady/=lib/solady/src/
forge-std/=lib/forge-std/src/
82 changes: 82 additions & 0 deletions src/Auction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ pragma solidity >=0.8.15;
import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {Multicallable} from "solady/utils/Multicallable.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {EthereumDecoder} from "./mpt/EthereumDecoder.sol";
import {VerifyMPTBalance} from "./mpt/VerifyMPTBalance.sol";
import {MPT} from "./mpt/MPT.sol";

/// @author philogy <https://github.com/philogy>
/// @author (contributor) 0xBeans <https://github.com/0xBeans>
contract Auction is Multicallable {
using EthereumDecoder for EthereumDecoder.BlockHeader;

Expand All @@ -30,6 +32,7 @@ contract Auction is Multicallable {
uint256 totalPendingAmount
);
event WinClaimed(address indexed winner, uint256 paidBid, uint256 refund);
event Slashed(address indexed bidder, uint256 paidBid, uint256 refund);

error AlreadyInitialized();
error InvalidRevealStartBlock();
Expand All @@ -39,6 +42,7 @@ contract Auction is Multicallable {
error NotOwner();

error RevealAlreadyStarted();
error RevealInProgress();
error NotYetRevealBlock();
error NotYetReveal();
error RevealOver();
Expand All @@ -51,6 +55,7 @@ contract Auction is Multicallable {
uint256 internal constant BID_EXTRACTOR_CODE = 0x3d3d3d3d47335af1;
uint256 internal constant BID_EXTRACTOR_CODE_SIZE = 0x8;
uint256 internal constant BID_EXTRACTOR_CODE_OFFSET = 0x18;
uint256 internal constant SLASH_AMT = 0.33e18; // amount to slash for late reveal

mapping(address => uint256) public pendingPulls;

Expand Down Expand Up @@ -184,6 +189,83 @@ contract Auction is Multicallable {
);
}

/*
* @notice For any reveals that happen after the alloted reveal time.
* @dev Slashing will occur in certain instances. No need to check proof.
* */
function lateReveal(
address _bidder,
uint256 _bid,
bytes32 _subSalt
) external returns (address bidAddr) {
uint256 totalBid;
uint256 refundAmt;

if (revealStartBlock + 7200 > block.number) {
revert RevealInProgress();
}

bytes32 storedBlockHashCached = storedBlockHash;
(bytes32 salt, address depositAddr) = getBidDepositAddr(
_bidder,
_bid,
_subSalt
);

uint256 balBefore = address(this).balance;
assembly {
mstore(0x00, BID_EXTRACTOR_CODE)
bidAddr := create2(
0,
BID_EXTRACTOR_CODE_OFFSET,
BID_EXTRACTOR_CODE_SIZE,
salt
)
}
totalBid = address(this).balance - balBefore;

unchecked {
// see if bidder needs to get slashed
refundAmt = totalBid - _getSlashAmt(totalBid);
}

_asyncSend(_bidder, refundAmt);

emit Slashed(_bidder, totalBid, refundAmt);
}

/*
* @dev Slashing logic.
* If bid < sndBid, it means the bidder would not have affected the auction in any way
* even if they revealed on time - return all funds with no slashing.
*
* If bid > sndBid && bid < topBid, this bidder would have affected the auction but would not have won.
* The auction house lost `_bid - snd` in value due to late reveal - slash the amount the auction would have received.
*
* If bid > topBid, this bidder affected the auction immensely as they would have won the auction. Slash the amount
* the auction house lost along with a 33% additional slash to completely disinsentivize late reveals for winning bids.
* */
function _getSlashAmt(uint256 _bid) internal returns (uint256 slashAmt) {
unchecked {
if (_bid > topBid) {
uint256 difference = topBid - sndBid;
uint256 amtAfterSlash = _bid - difference;

// update topBid incase newer late reveals are higher than this amount
topBid = uint128(_bid);

slashAmt =
difference +
FixedPointMathLib.mulWadDown(amtAfterSlash, SLASH_AMT);
} else if (_bid > sndBid) {
slashAmt = _bid - sndBid;

// update sndBid incase newer late reveals are higher than this amount
sndBid = uint128(_bid);
}
}
}

function claimWin(address _collection, uint256 _tokenId) external {
if (revealStartBlock + 7200 >= block.number) revert RevealNotOver();
bytes32 passedTokenCommit;
Expand Down
87 changes: 87 additions & 0 deletions test/Auction.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,91 @@ contract AuctionTest is BaseTest {
a = Auction(payable(Clones.clone(baseAuction)));
a.initialize(_initialOwner, _revealStartBlock, _tokenCommit);
}

function testLateReveal() public {
// create auction
uint256 revealStartBlock = block.number + 100;
Auction auction = createAuction(users[4], revealStartBlock, bytes32(0));

uint256[] memory bids = new uint256[](5);
bids[0] = 10e18; // bidder will reveal on time
bids[1] = 12e18; // bidder will reveal on time
bids[2] = 11e18; // bidder will not reveal on time
bids[3] = 7e18; // bidder will not reveal on time
bids[4] = 17e18; // bidder will not reveal on time

// create and fund bids
bytes32[5] memory subSalts;
for (uint256 i; i < 5; i++) {
subSalts[i] = genBytes32();
(, address bidAddr) = auction.getBidDepositAddr(
users[i],
bids[i],
subSalts[i]
);
vm.deal(bidAddr, bids[i]);
}

vm.roll(revealStartBlock + 1);
auction.startReveal();

// reveal for users[0]
auction.reveal(
users[0],
bids[0],
subSalts[0],
bids[0],
emptyHeader,
emptyProof
);

// reveal for users[1]
auction.reveal(
users[1],
bids[1],
subSalts[1],
bids[1],
emptyHeader,
emptyProof
);

// assert users[1] is top bidder
assertEq(auction.topBidder(), users[1]);
assertEq(auction.topBid(), bids[1]);

// assert users[0] bid is second top bid
assertEq(auction.sndBid(), bids[0]);

// roll pass the reveal time, not more bids can be revealed
vm.roll(revealStartBlock + 7201);

// late reveal bidders that did not reveal in the correct reveal phase
// should be slashed
for (uint256 i; i < 5; i++) {
auction.lateReveal(users[i], bids[i], subSalts[i]);
}

// pull funds
for (uint256 i; i < 5; i++) {
auction.pull(users[i]);
}

// should get full refund as they did not win the auction
assertEq(users[0].balance, 10e18);

// should be 0 since they won the auction
assertEq(users[1].balance, 0);

// this person would've affected the auction if they revealed on time
/// should be get slashed since they revealed late, bid - sndBid
assertEq(users[2].balance, 10e18);

// this bidder would not have affected auction even if they revealed on time
// no slashing
assertEq(users[3].balance, 7e18);

// this bidder would have won but they revealed late,
// should be get slashed significantly
assertEq(users[4].balance, 10.72e18);
}
}

0 comments on commit a744b42

Please sign in to comment.