Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
226 changes: 162 additions & 64 deletions contracts/BidderRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.15;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IBidderRegistry} from "./interfaces/IBidderRegistry.sol";
import {IBlockTracker} from "./interfaces/IBlockTracker.sol";

/// @title Bidder Registry
/// @author Kartik Chopra
Expand All @@ -16,8 +17,8 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
/// @dev Fee percent that would be taken by protocol when provider is slashed
uint16 public feePercent;

/// @dev Minimum prepay required for registration
uint256 public minAllowance;
/// @dev Minimum deposit required for registration
uint256 public minDeposit;

/// @dev Amount assigned to feeRecipient
uint256 public feeRecipientAmount;
Expand All @@ -28,26 +29,54 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
/// @dev Address of the pre-confirmations contract
address public preConfirmationsContract;

/// @dev Block tracker contract
IBlockTracker public blockTrackerContract;

/// @dev Fee recipient
address public feeRecipient;

/// @dev Mapping for if bidder is registered
mapping(address => bool) public bidderRegistered;

/// @dev Mapping from bidder addresses to their prepayed amount
mapping(address => uint256) public bidderPrepaidBalances;
// Mapping from bidder addresses and window numbers to their locked funds
mapping(address => mapping(uint256 => uint256)) public lockedFunds;

/// @dev Mapping from bidder addresses to their locked amount based on bidID (commitmentDigest)
mapping(bytes32 => BidState) public BidPayment;

/// @dev Amount assigned to bidders
mapping(address => uint256) public providerAmount;

/// @dev Event emitted when a bidder is registered with their prepayed amount
event BidderRegistered(address indexed bidder, uint256 prepaidAmount);

/// @dev Event emitted when funds are retrieved from a bidder's prepay
event FundsRetrieved(bytes32 indexed commitmentDigest, uint256 amount);
/// @dev Event emitted when a bidder is registered with their deposited amount
event BidderRegistered(
address indexed bidder,
uint256 depositedAmount,
uint256 windowNumber
);

/// @dev Event emitted when funds are retrieved from a bidder's deposit
event FundsRetrieved(
bytes32 indexed commitmentDigest,
address indexed bidder,
uint256 window,
uint256 amount
);

/// @dev Event emitted when funds are retrieved from a bidder's deposit
event FundsRewarded(
bytes32 indexed commitmentDigest,
address indexed bidder,
address indexed provider,
uint256 window,
uint256 amount
);

/// @dev Event emitted when a bidder withdraws their deposit
event BidderWithdrawal(
address indexed bidder,
uint256 window,
uint256 amount
);

/**
* @dev Fallback function to revert all calls, ensuring no unintended interactions.
Expand All @@ -57,29 +86,31 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
}

/**
* @dev Receive function registers bidders and takes their prepay
* Should be removed from here in case the prepay function becomes more complex
* @dev Receive function registers bidders and takes their deposit
* Should be removed from here in case the deposit function becomes more complex
*/
receive() external payable {
prepay();
revert("Invalid call");
}

/**
* @dev Constructor to initialize the contract with a minimum prepay requirement.
* @param _minAllowance The minimum prepay required for bidder registration.
* @dev Constructor to initialize the contract with a minimum deposit requirement.
* @param _minDeposit The minimum deposit required for bidder registration.
* @param _feeRecipient The address that receives fee
* @param _feePercent The fee percentage for protocol
* @param _owner Owner of the contract, explicitly needed since contract is deployed w/ create2 factory.
*/
constructor(
uint256 _minAllowance,
uint256 _minDeposit,
address _feeRecipient,
uint16 _feePercent,
address _owner
address _owner,
address _blockTracker
) {
minAllowance = _minAllowance;
minDeposit = _minDeposit;
feeRecipient = _feeRecipient;
feePercent = _feePercent;
blockTrackerContract = IBlockTracker(_blockTracker);
_transferOwnership(_owner);
}

Expand Down Expand Up @@ -111,67 +142,67 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
/**
* @dev Get the amount assigned to a provider.
*/
function getProviderAmount(address provider) external view returns (uint256) {
function getProviderAmount(
address provider
) external view returns (uint256) {
return providerAmount[provider];
}

/**
* @dev Get the amount assigned to the fee recipient (treasury).
*/
function getFeeRecipientAmount() external onlyOwner view returns (uint256) {
function getFeeRecipientAmount() external view onlyOwner returns (uint256) {
return feeRecipientAmount;
}

/**
* @dev Internal function for bidder registration and staking.
* @dev Deposit for a specific window.
* @param window The window for which the deposit is being made.
*/
function prepay() public payable {
require(msg.value >= minAllowance, "Insufficient prepay");
function depositForSpecificWindow(uint256 window) external payable {
require(msg.value >= minDeposit, "Insufficient deposit");

bidderPrepaidBalances[msg.sender] += msg.value;
bidderRegistered[msg.sender] = true;
lockedFunds[msg.sender][window] += msg.value;

emit BidderRegistered(msg.sender, bidderPrepaidBalances[msg.sender]);
emit BidderRegistered(msg.sender, lockedFunds[msg.sender][window], window);
}

/**
* @dev Check the prepay of a bidder.
* @dev Check the deposit of a bidder.
* @param bidder The address of the bidder.
* @return The prepayed amount for the bidder.
* @return The deposited amount for the bidder.
*/
function getAllowance(address bidder) external view returns (uint256) {
return bidderPrepaidBalances[bidder];
}

function LockBidFunds(bytes32 commitmentDigest, uint64 bid, address bidder) external onlyPreConfirmationEngine(){
BidState memory bidState = BidPayment[commitmentDigest];
if (bidState.state == State.Undefined) {
BidPayment[commitmentDigest] = BidState({
bidAmt: bid,
state: State.PreConfirmed,
bidder: bidder
});
bidderPrepaidBalances[bidder] -= bid;
}
function getDeposit(
address bidder,
uint256 window
) external view returns (uint256) {
return lockedFunds[bidder][window];
}

/**
* @dev Retrieve funds from a bidder's prepay (only callable by the pre-confirmations contract).
* @dev Retrieve funds from a bidder's deposit (only callable by the pre-confirmations contract).
* @dev reenterancy not necessary but still putting here for precaution
* @param commitmentDigest is the Bid ID that allows us to identify the bid, and prepayment
* @param commitmentDigest is the Bid ID that allows us to identify the bid, and deposit
* @param provider The address to transfer the retrieved funds to.
*/
function retrieveFunds(
uint256 windowToSettle,
bytes32 commitmentDigest,
address payable provider,
uint256 residualBidPercentAfterDecay
) external nonReentrant onlyPreConfirmationEngine {

BidState memory bidState = BidPayment[commitmentDigest];
require(bidState.state == State.PreConfirmed, "The bid was not preconfirmed");
uint256 decayedAmt = ( bidState.bidAmt * residualBidPercentAfterDecay * PRECISION) / PERCENT;
require(
bidState.state == State.PreConfirmed,
"The bid was not preconfirmed"
);
uint256 decayedAmt = (bidState.bidAmt *
residualBidPercentAfterDecay *
PRECISION) / PERCENT;

uint256 feeAmt = (decayedAmt * uint256(feePercent) * PRECISION) / PERCENT;
uint256 feeAmt = (decayedAmt * uint256(feePercent) * PRECISION) /
PERCENT;
uint256 amtMinusFeeAndDecay = decayedAmt - feeAmt;

if (feeRecipient != address(0)) {
Expand All @@ -183,30 +214,68 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
providerAmount[provider] += amtMinusFeeAndDecay;

// Ensures the bidder gets back the bid amount - decayed reward given to provider and protocol
bidderPrepaidBalances[bidState.bidder] += bidState.bidAmt - decayedAmt;
lockedFunds[bidState.bidder][windowToSettle] +=
bidState.bidAmt -
decayedAmt;

BidPayment[commitmentDigest].state = State.Withdrawn;
BidPayment[commitmentDigest].bidAmt = 0;

emit FundsRetrieved(commitmentDigest, decayedAmt);
emit FundsRewarded(
commitmentDigest,
bidState.bidder,
provider,
windowToSettle,
decayedAmt
);
}

/**
* @dev Return funds to a bidder's prepay (only callable by the pre-confirmations contract).
* @dev Return funds to a bidder's deposit (only callable by the pre-confirmations contract).
* @dev reenterancy not necessary but still putting here for precaution
* @param bidID is the Bid ID that allows us to identify the bid, and prepayment
* @param bidID is the Bid ID that allows us to identify the bid, and deposit
*/
function unlockFunds(bytes32 bidID) external nonReentrant onlyPreConfirmationEngine() {
function unlockFunds(uint256 window, bytes32 bidID) external nonReentrant onlyPreConfirmationEngine() {
BidState memory bidState = BidPayment[bidID];
require(bidState.state == State.PreConfirmed, "The bid was not preconfirmed");
uint256 amt = bidState.bidAmt;
bidderPrepaidBalances[bidState.bidder] += amt;

lockedFunds[bidState.bidder][window] += amt;

BidPayment[bidID].state = State.Withdrawn;
BidPayment[bidID].bidAmt = 0;

emit FundsRetrieved(bidID, amt);

emit FundsRetrieved(bidID, bidState.bidder, window, amt);
}

/**
* @dev Open a bid (only callable by the pre-confirmations contract).
* @param commitmentDigest is the Bid ID that allows us to identify the bid, and deposit
* @param bid The bid amount.
* @param bidder The address of the bidder.
*/
function OpenBid(
bytes32 commitmentDigest,
uint64 bid,
address bidder,
uint64 blockNumber
) external onlyPreConfirmationEngine {
BidState memory bidState = BidPayment[commitmentDigest];
if (bidState.state == State.Undefined) {
uint256 currentWindow = blockTrackerContract.getWindowFromBlockNumber(blockNumber);
// @todo delete this, when oracle will do the calculation
// bidder cannot bid more than allowed for the round
uint256 numberOfRounds = blockTrackerContract.getBlocksPerWindow();
uint256 windowAmount = lockedFunds[bidder][currentWindow] / numberOfRounds;
if (windowAmount < bid) {
bid = uint64(windowAmount);
}
BidPayment[commitmentDigest] = BidState({
state: State.PreConfirmed,
bidder: bidder,
bidAmt: bid
});
lockedFunds[bidder][currentWindow] -= bid;
}
}

/**
Expand All @@ -227,6 +296,9 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
feePercent = newFeePercent;
}

/**
* @dev Withdraw funds to the fee recipient.
*/
function withdrawFeeRecipientAmount() external nonReentrant {
uint256 amount = feeRecipientAmount;
feeRecipientAmount = 0;
Expand All @@ -235,6 +307,10 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
require(successFee, "couldn't transfer to fee Recipient");
}

/**
* @dev Withdraw funds to the provider.
* @param provider The address of the provider.
*/
function withdrawProviderAmount(
address payable provider
) external nonReentrant {
Expand All @@ -246,16 +322,38 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
require(success, "couldn't transfer to provider");
}

function withdrawPrepaidAmount(address payable bidder) external nonReentrant {
uint256 prepaidAmount = bidderPrepaidBalances[bidder];
bidderPrepaidBalances[bidder] = 0;
require(msg.sender == bidder, "only bidder can unprepay");
require(prepaidAmount > 0, "bidder prepaid Amount is zero");
/**
* @dev Withdraw funds to the bidder.
* @param bidder The address of the bidder.
*/
function withdrawBidderAmountFromWindow(
address payable bidder,
uint256 window
) external nonReentrant {
require(
msg.sender == bidder,
"only bidder can withdraw funds from window"
);
uint256 currentWindow = blockTrackerContract.getCurrentWindow();
// withdraw is enabled only when closed and settled
require(
window < currentWindow,
"funds can only be withdrawn after the window is settled"
);
uint256 amount = lockedFunds[bidder][window];
lockedFunds[bidder][window] = 0;
require(amount > 0, "bidder Amount is zero");

(bool success, ) = bidder.call{value: amount}("");
require(success, "couldn't transfer to bidder");

(bool success, ) = bidder.call{value: prepaidAmount}("");
require(success, "couldn't transfer prepay to bidder");
emit BidderWithdrawal(bidder, window, amount);
}

/**
* @dev Withdraw protocol fee.
* @param bidder The address of the bidder.
*/
function withdrawProtocolFee(
address payable bidder
) external onlyOwner nonReentrant {
Expand All @@ -264,6 +362,6 @@ contract BidderRegistry is IBidderRegistry, Ownable, ReentrancyGuard {
require(_protocolFeeAmount > 0, "insufficient protocol fee amount");

(bool success, ) = bidder.call{value: _protocolFeeAmount}("");
require(success, "couldn't transfer prepay to bidder");
require(success, "couldn't transfer deposit to bidder");
}
}
Loading