Skip to content

Commit

Permalink
Add optional fees to Auction contract
Browse files Browse the repository at this point in the history
  • Loading branch information
neokry committed Jul 27, 2023
1 parent 682d317 commit 34d47d5
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 7 deletions.
5 changes: 4 additions & 1 deletion script/DeployContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "forge-std/Script.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";

import { IManager, Manager } from "../src/manager/Manager.sol";
import { BuilderFeeManager } from "../src/fees/BuilderFeeManager.sol";
import { IToken, Token } from "../src/token/Token.sol";
import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol";
import { IAuction, Auction } from "../src/auction/Auction.sol";
Expand Down Expand Up @@ -48,8 +49,10 @@ contract DeployContracts is Script {
// Deploy metadata renderer implementation
address metadataRendererImpl = address(new MetadataRenderer(address(manager)));

address feeManager = address(new BuilderFeeManager(0, address(0)));

// Deploy auction house implementation
address auctionImpl = address(new Auction(address(manager), weth));
address auctionImpl = address(new Auction(address(manager), feeManager, weth));

// Deploy treasury implementation
address treasuryImpl = address(new Treasury(address(manager)));
Expand Down
5 changes: 4 additions & 1 deletion script/DeployVersion1_1.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "forge-std/console2.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";

import { IManager, Manager } from "../src/manager/Manager.sol";
import { BuilderFeeManager } from "../src/fees/BuilderFeeManager.sol";
import { IToken, Token } from "../src/token/Token.sol";
import { MetadataRenderer } from "../src/token/metadata/MetadataRenderer.sol";
import { IAuction, Auction } from "../src/auction/Auction.sol";
Expand Down Expand Up @@ -49,8 +50,10 @@ contract DeployVersion1_1 is Script {
// Get root manager implementation + proxy
Manager manager = Manager(managerProxy);

address feeManager = address(new BuilderFeeManager(0, address(0)));

// Deploy auction upgrade implementation
address auctionUpgradeImpl = address(new Auction(managerProxy, weth));
address auctionUpgradeImpl = address(new Auction(managerProxy, feeManager, weth));
// Deploy governor upgrade implementation
address governorUpgradeImpl = address(new Governor(managerProxy));
// Deploy treasury upgrade implementation
Expand Down
35 changes: 31 additions & 4 deletions src/auction/Auction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SafeCast } from "../lib/utils/SafeCast.sol";
import { AuctionStorageV1 } from "./storage/AuctionStorageV1.sol";
import { Token } from "../token/Token.sol";
import { IManager } from "../manager/IManager.sol";
import { IBuilderFeeManager } from "../fees/interfaces/IBuilderFeeManager.sol";
import { IAuction } from "./IAuction.sol";
import { IWETH } from "../lib/interfaces/IWETH.sol";

Expand All @@ -36,17 +37,22 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard,
/// @notice The address of WETH
address private immutable WETH;

/// @notice The builder fee manager
address private immutable builderFeeManager;

/// @notice The contract upgrade manager
IManager private immutable manager;

/// ///
/// CONSTRUCTOR ///
/// ///

/// @param _builderFeeManager The builder fee manager address
/// @param _manager The contract upgrade manager address
/// @param _weth The address of WETH
constructor(address _manager, address _weth) payable initializer {
constructor(address _manager, address _builderFeeManager, address _weth) payable initializer {
manager = IManager(_manager);
builderFeeManager = _builderFeeManager;
WETH = _weth;
}

Expand Down Expand Up @@ -196,10 +202,24 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard,
// If a bid was placed:
if (_auction.highestBidder != address(0)) {
// Cache the amount of the highest bid
uint256 highestBid = _auction.highestBid;
uint256 funds = _auction.highestBid;

// Check if fee manager is set
if (builderFeeManager != address(0)) {
// Get the fee amount from the fee manager
(address recipent, uint256 fee) = _builderFeeForAmount(funds);

// If there is a fee to be payed
if (fee != 0) {
_handleOutgoingTransfer(recipent, fee);
funds -= fee;
}
}

// If the highest bid included ETH: Transfer it to the DAO treasury
if (highestBid != 0) _handleOutgoingTransfer(settings.treasury, highestBid);
// If the highest bid included ETH: Transfer remaining funds to the DAO treasury
if (funds != 0) {
_handleOutgoingTransfer(settings.treasury, funds);
}

// Transfer the token to the highest bidder
token.transferFrom(address(this), _auction.highestBidder, _auction.tokenId);
Expand Down Expand Up @@ -361,6 +381,13 @@ contract Auction is IAuction, VersionedContract, UUPS, Ownable, ReentrancyGuard,
emit MinBidIncrementPercentageUpdated(_percentage);
}

/// @dev Gets the builder fee for amount of withdraw
/// @param amount amount of funds to get fee for
function _builderFeeForAmount(uint256 amount) private returns (address payable, uint256) {
(address payable recipient, uint256 bps) = IBuilderFeeManager(builderFeeManager).getBuilderFeesBPS(address(this));
return (recipient, (amount * bps) / 10_000);
}

/// ///
/// TRANSFER UTIL ///
/// ///
Expand Down
37 changes: 37 additions & 0 deletions src/fees/BuilderFeeManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import { IBuilderFeeManager } from "./interfaces/IBuilderFeeManager.sol";
import { Ownable } from "../lib/utils/Ownable.sol";

contract BuilderFeeManager is Ownable, IBuilderFeeManager {
mapping(address => uint256) private feeOverride;
uint256 private defaultFeeBPS;

event FeeOverrideSet(address indexed, uint256 indexed);
event DefaultFeeSet(uint256 indexed);

constructor(uint256 _defaultFeeBPS, address feeManagerAdmin) {
defaultFeeBPS = _defaultFeeBPS;
_transferOwnership(feeManagerAdmin);
}

function setDefaultFee(uint256 amountBPS) external onlyOwner {
require(amountBPS < 2001, "Fee too high (not greater than 20%)");
defaultFeeBPS = amountBPS;
emit DefaultFeeSet(amountBPS);
}

function setFeeOverride(address tokenContract, uint256 amountBPS) external onlyOwner {
require(amountBPS < 2001, "Fee too high (not greater than 20%)");
feeOverride[tokenContract] = amountBPS;
emit FeeOverrideSet(tokenContract, amountBPS);
}

function getBuilderFeesBPS(address mediaContract) external view returns (address payable, uint256) {
if (feeOverride[mediaContract] > 0) {
return (payable(owner()), feeOverride[mediaContract]);
}
return (payable(owner()), defaultFeeBPS);
}
}
6 changes: 6 additions & 0 deletions src/fees/interfaces/IBuilderFeeManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IBuilderFeeManager {
function getBuilderFeesBPS(address sender) external returns (address payable, uint256);
}
5 changes: 4 additions & 1 deletion test/utils/NounsBuilderTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.16;
import { Test } from "forge-std/Test.sol";

import { IManager, Manager } from "../../src/manager/Manager.sol";
import { BuilderFeeManager } from "../../src/fees/BuilderFeeManager.sol";
import { IToken, Token } from "../../src/token/Token.sol";
import { IBaseMetadata, MetadataRenderer } from "../../src/token/metadata/MetadataRenderer.sol";
import { IAuction, Auction } from "../../src/auction/Auction.sol";
Expand All @@ -25,6 +26,7 @@ contract NounsBuilderTest is Test {

address internal managerImpl0;
address internal managerImpl;
address internal feeManager;
address internal tokenImpl;
address internal metadataRendererImpl;
address internal auctionImpl;
Expand Down Expand Up @@ -60,10 +62,11 @@ contract NounsBuilderTest is Test {

managerImpl0 = address(new Manager());
manager = Manager(address(new ERC1967Proxy(managerImpl0, abi.encodeWithSignature("initialize(address)", zoraDAO))));
feeManager = address(new BuilderFeeManager(0, address(0)));

tokenImpl = address(new Token(address(manager)));
metadataRendererImpl = address(new MetadataRenderer(address(manager)));
auctionImpl = address(new Auction(address(manager), weth));
auctionImpl = address(new Auction(address(manager), feeManager, weth));
treasuryImpl = address(new Treasury(address(manager)));
governorImpl = address(new Governor(address(manager)));

Expand Down

0 comments on commit 34d47d5

Please sign in to comment.