Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"project/contracts/HarbergerFactory.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.28;
import {ValuationTaxEnabledSlot} from "./ValuationTaxEnabledSlot.sol";
import {ValuationTaxShieldedSlot} from "./ValuationTaxShieldedSlot.sol";
/**
* @title HarbergerFactory
* @notice Deploys Harberger ad slots and tracks their metadata.
*/
contract HarbergerFactory {
uint256 private constant RATE_DENOMINATOR = 1e4; // basis points
error Unauthorized();
error InvalidParameter();
error GlobalAddressesUnset();
error SlotDoesNotExist();
error NoPendingOwner();
enum SlotType {
Unknown,
ValuationTaxEnabled,
ValuationTaxShielded
}
struct SlotInfo {
address slotAddress;
SlotType slotType;
}
address public owner;
address public pendingOwner;
address public treasury;
address public governance;
uint256 public slotIdCounter;
mapping(uint256 => SlotInfo) private _slots;
mapping(address => SlotType) public slotTypeByAddress;
address[] private _valuationTaxEnabledSlots;
address[] private _valuationTaxShieldedSlots;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
event GlobalAddressesConfigured(address indexed treasury, address indexed governance);
event ValuationTaxEnabledSlotCreated(uint256 indexed slotId, address slotAddress);
event ValuationTaxShieldedSlotCreated(uint256 indexed slotId, address slotAddress);
modifier onlyOwner() {
if (msg.sender != owner) revert Unauthorized();
_;
}
constructor(address treasury_, address governance_) {
owner = msg.sender;
if (treasury_ != address(0) && governance_ != address(0)) {
treasury = treasury_;
governance = governance_;
emit GlobalAddressesConfigured(treasury_, governance_);
}
}
function setGlobalAddresses(address treasury_, address governance_) external onlyOwner {
if (treasury_ == address(0) || governance_ == address(0)) revert InvalidParameter();
treasury = treasury_;
governance = governance_;
emit GlobalAddressesConfigured(treasury_, governance_);
}
function transferOwnership(address newOwner) external onlyOwner {
if (newOwner == address(0)) revert InvalidParameter();
pendingOwner = newOwner;
emit OwnershipTransferStarted(owner, newOwner);
}
function acceptOwnership() external {
address newOwner = pendingOwner;
if (newOwner == address(0)) revert NoPendingOwner();
if (msg.sender != newOwner) revert Unauthorized();
address previousOwner = owner;
owner = newOwner;
pendingOwner = address(0);
emit OwnershipTransferred(previousOwner, newOwner);
}
function createValuationTaxEnabledSlot(
uint256 bondRate,
uint256 contentUpdateLimit,
uint256 taxPeriodInSeconds,
uint256 annualTaxRate,
uint256 minBidIncrementRate,
uint256 minValuation,
uint256 dustRate
) external onlyOwner returns (uint256 slotId, address slotAddress) {
if (treasury == address(0) || governance == address(0)) revert GlobalAddressesUnset();
if (taxPeriodInSeconds == 0 || annualTaxRate == 0) revert InvalidParameter();
if (bondRate == 0 || bondRate > RATE_DENOMINATOR) revert InvalidParameter();
if (minValuation == 0) revert InvalidParameter();
if (dustRate > RATE_DENOMINATOR) revert InvalidParameter();
slotId = ++slotIdCounter;
ValuationTaxEnabledSlot slotInstance = new ValuationTaxEnabledSlot(
treasury,
governance,
bondRate,
contentUpdateLimit,
taxPeriodInSeconds,
annualTaxRate,
minBidIncrementRate,
minValuation,
dustRate
);
slotAddress = address(slotInstance);
_slots[slotId] = SlotInfo({slotAddress: slotAddress, slotType: SlotType.ValuationTaxEnabled});
slotTypeByAddress[slotAddress] = SlotType.ValuationTaxEnabled;
_valuationTaxEnabledSlots.push(slotAddress);
emit ValuationTaxEnabledSlotCreated(slotId, slotAddress);
}
function createValuationTaxShieldedSlot(
uint256 bondRate,
uint256 contentUpdateLimit,
uint256 taxPeriodInSeconds,
uint256 annualTaxRate,
uint256 minBidIncrementRate,
uint256 minValuation
) external onlyOwner returns (uint256 slotId, address slotAddress) {
if (treasury == address(0) || governance == address(0)) revert GlobalAddressesUnset();
if (taxPeriodInSeconds == 0 || annualTaxRate == 0) revert InvalidParameter();
if (bondRate == 0 || bondRate > RATE_DENOMINATOR) revert InvalidParameter();
if (minValuation == 0) revert InvalidParameter();
slotId = ++slotIdCounter;
ValuationTaxShieldedSlot slotInstance = new ValuationTaxShieldedSlot(
treasury,
governance,
bondRate,
contentUpdateLimit,
taxPeriodInSeconds,
annualTaxRate,
minBidIncrementRate,
minValuation
);
slotAddress = address(slotInstance);
_slots[slotId] = SlotInfo({slotAddress: slotAddress, slotType: SlotType.ValuationTaxShielded});
slotTypeByAddress[slotAddress] = SlotType.ValuationTaxShielded;
_valuationTaxShieldedSlots.push(slotAddress);
emit ValuationTaxShieldedSlotCreated(slotId, slotAddress);
}
function getSlot(uint256 slotId) external view returns (SlotInfo memory info) {
info = _slots[slotId];
if (info.slotAddress == address(0)) revert SlotDoesNotExist();
}
function getValuationTaxEnabledSlots() external view returns (address[] memory slots_) {
slots_ = _valuationTaxEnabledSlots;
}
function getValuationTaxShieldedSlots() external view returns (address[] memory slots_) {
slots_ = _valuationTaxShieldedSlots;
}
}
"
},
"project/contracts/ValuationTaxEnabledSlot.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.28;
/**
* @title ValuationTaxEnabledSlot
* @notice Harberger-style ad slot variant where only a fraction of the valuation is bonded on-chain.
*/
contract ValuationTaxEnabledSlot {
uint256 private constant RATE_DENOMINATOR = 1e4; // basis points (10000 = 100%)
uint256 private constant SECONDS_PER_YEAR = 365 days;
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private constant TAX_BASE = RATE_DENOMINATOR * SECONDS_PER_YEAR;
error Unauthorized();
error SlotOccupied();
error SlotVacant();
error InvalidAmount();
error InvalidParameter();
error ContentUpdateLimitReached();
error TaxStillActive();
error MinIncrementNotMet();
error TransferFailed();
error Reentrancy();
event SlotClaimed(address indexed newOwner, uint256 valuation, uint256 lockedAmount, uint256 paidThrough);
event SlotTakenOver(
address indexed previousOwner,
address indexed newOwner,
uint256 newValuation,
uint256 lockedAmount,
uint256 paidThrough
);
event SlotRenewed(address indexed owner, uint256 paidThrough);
event SlotForfeited(address indexed previousOwner);
event SlotReset(address indexed previousOwner);
event AdCreativeUpdated(address indexed owner, string uri);
event SlotPoked(address indexed operator, uint256 taxPaidUntil, uint256 valuationAfter);
event TreasuryPayment(uint256 amount);
event FundsSent(address indexed to, uint256 amount);
address public immutable treasury;
address public immutable governance;
address public immutable factory;
uint256 public immutable bondRate;
uint256 public immutable contentUpdateLimit;
uint256 public immutable taxPeriodInSeconds;
uint256 public immutable annualTaxRate;
uint256 public immutable minBidIncrementRate;
uint256 public immutable minValuation;
uint256 public immutable dustRate;
address public currentOwner;
uint256 public valuation;
uint256 public lockedValuation;
uint256 public baseValuation;
uint256 public taxPaidUntil;
uint256 public prepaidTaxBalance;
uint256 public contentUpdateCount;
string public currentAdURI;
uint256 private _status;
struct SlotDetails {
address currentOwner;
uint256 valuation;
uint256 lockedValuation;
uint256 prepaidTaxBalance;
uint256 taxPaidUntil;
uint256 timeRemainingInSeconds;
uint256 contentUpdateCount;
uint256 contentUpdateLimit;
uint256 taxPeriodInSeconds;
uint256 annualTaxRate;
uint256 minBidIncrementRate;
uint256 bondRate;
uint256 minValuation;
uint256 baseValuation;
uint256 dustRate;
string currentAdURI;
address treasury;
address governance;
bool isOccupied;
}
constructor(
address treasury_,
address governance_,
uint256 bondRate_,
uint256 contentUpdateLimit_,
uint256 taxPeriodInSeconds_,
uint256 annualTaxRate_,
uint256 minBidIncrementRate_,
uint256 minValuation_,
uint256 dustRate_
) {
if (treasury_ == address(0) || governance_ == address(0)) revert InvalidParameter();
if (taxPeriodInSeconds_ == 0 || annualTaxRate_ == 0) revert InvalidParameter();
if (bondRate_ == 0 || bondRate_ > RATE_DENOMINATOR) revert InvalidParameter();
if (minValuation_ == 0) revert InvalidParameter();
if (dustRate_ > RATE_DENOMINATOR) revert InvalidParameter();
treasury = treasury_;
governance = governance_;
factory = msg.sender;
bondRate = bondRate_;
contentUpdateLimit = contentUpdateLimit_;
taxPeriodInSeconds = taxPeriodInSeconds_;
annualTaxRate = annualTaxRate_;
minBidIncrementRate = minBidIncrementRate_;
minValuation = minValuation_;
dustRate = dustRate_;
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
if (_status == _ENTERED) revert Reentrancy();
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
modifier onlyOwner() {
if (msg.sender != currentOwner) revert Unauthorized();
_;
}
modifier onlyGovernance() {
if (msg.sender != governance) revert Unauthorized();
_;
}
function claim(uint256 newValuation, uint256 taxPeriods, string calldata newUri)
external
payable
nonReentrant
{
if (currentOwner != address(0)) revert SlotOccupied();
if (newValuation == 0 || taxPeriods == 0) revert InvalidParameter();
if (newValuation < minValuation) revert InvalidParameter();
uint256 lockedAmount = _calculateLockedAmount(newValuation);
uint256 taxDue = _calculateTax(newValuation, taxPeriods);
if (taxDue == 0) revert InvalidParameter();
uint256 requiredValue = lockedAmount + taxDue;
if (msg.value != requiredValue) revert InvalidAmount();
uint256 coverage = taxPeriodInSeconds * taxPeriods;
lockedValuation = lockedAmount;
valuation = newValuation;
baseValuation = newValuation;
currentOwner = msg.sender;
taxPaidUntil = block.timestamp + coverage;
contentUpdateCount = 0;
currentAdURI = newUri;
prepaidTaxBalance = taxDue;
emit SlotClaimed(msg.sender, newValuation, lockedAmount, taxPaidUntil);
}
function takeOver(uint256 newValuation, uint256 taxPeriods, string calldata newUri)
external
payable
nonReentrant
{
address previousOwner = currentOwner;
if (previousOwner == address(0)) revert SlotVacant();
_settleOverdueTax();
uint256 refundableTax = prepaidTaxBalance;
prepaidTaxBalance = 0;
if (taxPeriods == 0 || newValuation == 0) revert InvalidParameter();
if (newValuation < minValuation) revert InvalidParameter();
uint256 minValuationRequired = valuation + ((valuation * minBidIncrementRate) / RATE_DENOMINATOR);
if (newValuation < minValuationRequired) revert MinIncrementNotMet();
uint256 lockedAmount = _calculateLockedAmount(newValuation);
uint256 taxDue = _calculateTax(newValuation, taxPeriods);
if (taxDue == 0) revert InvalidParameter();
if (msg.value != lockedAmount + taxDue) revert InvalidAmount();
uint256 refundAmount = lockedValuation + refundableTax;
uint256 newPaidThrough = block.timestamp + (taxPeriodInSeconds * taxPeriods);
currentOwner = msg.sender;
valuation = newValuation;
lockedValuation = lockedAmount;
baseValuation = newValuation;
taxPaidUntil = newPaidThrough;
contentUpdateCount = 0;
currentAdURI = newUri;
prepaidTaxBalance = taxDue;
if (refundAmount != 0) {
_sendValue(payable(previousOwner), refundAmount);
}
emit SlotTakenOver(previousOwner, msg.sender, newValuation, lockedAmount, newPaidThrough);
}
function renew(uint256 taxPeriods) external payable nonReentrant onlyOwner {
if (taxPeriods == 0) revert InvalidParameter();
_settleOverdueTax();
uint256 taxDue = _calculateTax(valuation, taxPeriods);
if (taxDue == 0) revert InvalidParameter();
if (msg.value != taxDue) revert InvalidAmount();
uint256 coverage = taxPeriodInSeconds * taxPeriods;
taxPaidUntil = taxPaidUntil + coverage;
prepaidTaxBalance += taxDue;
emit SlotRenewed(msg.sender, taxPaidUntil);
}
function forfeit() external nonReentrant {
address previousOwner = currentOwner;
if (previousOwner == address(0)) revert SlotVacant();
_settleOverdueTax();
if (block.timestamp < taxPaidUntil) revert TaxStillActive();
uint256 payout = lockedValuation;
uint256 refundableTax = prepaidTaxBalance;
prepaidTaxBalance = 0;
currentOwner = address(0);
valuation = 0;
lockedValuation = 0;
baseValuation = 0;
taxPaidUntil = 0;
contentUpdateCount = 0;
currentAdURI = "";
_sendValue(payable(previousOwner), payout + refundableTax);
emit SlotForfeited(previousOwner);
}
function poke() external nonReentrant {
address previousOwner = currentOwner;
if (previousOwner == address(0)) revert SlotVacant();
if (block.timestamp < taxPaidUntil) revert TaxStillActive();
_settleOverdueTax();
if (lockedValuation == 0) {
currentOwner = address(0);
valuation = 0;
baseValuation = 0;
taxPaidUntil = 0;
contentUpdateCount = 0;
currentAdURI = "";
prepaidTaxBalance = 0;
emit SlotForfeited(previousOwner);
} else {
emit SlotPoked(msg.sender, taxPaidUntil, valuation);
}
}
function governanceReset() external nonReentrant onlyGovernance {
address previousOwner = currentOwner;
if (previousOwner == address(0)) revert SlotVacant();
_settleOverdueTax();
uint256 refundableTax = prepaidTaxBalance;
prepaidTaxBalance = 0;
uint256 payout = lockedValuation;
currentOwner = address(0);
valuation = 0;
lockedValuation = 0;
baseValuation = 0;
taxPaidUntil = 0;
contentUpdateCount = 0;
currentAdURI = "";
_sendValue(payable(previousOwner), payout + refundableTax);
emit SlotReset(previousOwner);
}
function updateAdCreative(string calldata newUri) external onlyOwner {
if (contentUpdateCount >= contentUpdateLimit) revert ContentUpdateLimitReached();
contentUpdateCount += 1;
currentAdURI = newUri;
emit AdCreativeUpdated(msg.sender, newUri);
}
function getSlotDetails() external view returns (SlotDetails memory details) {
details = SlotDetails({
currentOwner: currentOwner,
valuation: valuation,
lockedValuation: lockedValuation,
prepaidTaxBalance: prepaidTaxBalance,
taxPaidUntil: taxPaidUntil,
timeRemainingInSeconds: block.timestamp >= taxPaidUntil
? 0
: taxPaidUntil - block.timestamp,
contentUpdateCount: contentUpdateCount,
contentUpdateLimit: contentUpdateLimit,
taxPeriodInSeconds: taxPeriodInSeconds,
annualTaxRate: annualTaxRate,
minBidIncrementRate: minBidIncrementRate,
bondRate: bondRate,
minValuation: minValuation,
baseValuation: baseValuation,
dustRate: dustRate,
currentAdURI: currentAdURI,
treasury: treasury,
governance: governance,
isOccupied: currentOwner != address(0)
});
}
function _calculateTax(uint256 valuation_, uint256 periods) internal view returns (uint256) {
uint256 numerator = valuation_ * annualTaxRate * taxPeriodInSeconds * periods;
return numerator / TAX_BASE;
}
function _calculateLockedAmount(uint256 valuation_) internal view returns (uint256) {
return (valuation_ * bondRate) / RATE_DENOMINATOR;
}
// TODO: Evaluate introducing an idempotent closed-form settlement formula.
function _settleOverdueTax() private {
if (currentOwner == address(0)) return;
if (block.timestamp < taxPaidUntil) return;
uint256 taxFactor = annualTaxRate * taxPeriodInSeconds;
if (taxFactor == 0) return;
uint256 taxDenominator = TAX_BASE + taxFactor;
uint256 dustThresholdValuation;
if (dustRate != 0 && baseValuation != 0) {
dustThresholdValuation = (baseValuation * dustRate) / RATE_DENOMINATOR;
}
uint256 overdueSeconds = block.timestamp - taxPaidUntil;
uint256 periodsDue = (overdueSeconds / taxPeriodInSeconds) + 1;
uint256 taxAccrued;
uint256 ownerRefund;
for (uint256 i = 0; i < periodsDue; ++i) {
uint256 previousValuation = valuation;
uint256 previousLocked = lockedValuation;
if (previousValuation == 0 || previousLocked == 0) {
if (prepaidTaxBalance > 0) {
ownerRefund += prepaidTaxBalance;
prepaidTaxBalance = 0;
}
valuation = 0;
lockedValuation = 0;
baseValuation = 0;
taxPaidUntil = block.timestamp;
break;
}
uint256 theoreticalValuation = (previousValuation * TAX_BASE) / taxDenominator;
uint256 theoreticalTax = previousValuation - theoreticalValuation;
uint256 taxRemaining = theoreticalTax;
if (prepaidTaxBalance > 0) {
uint256 taxFromPrepaid = prepaidTaxBalance >= taxRemaining ? taxRemaining : prepaidTaxBalance;
prepaidTaxBalance -= taxFromPrepaid;
taxRemaining -= taxFromPrepaid;
taxAccrued += taxFromPrepaid;
}
uint256 taxFromLocked;
if (taxRemaining > 0) {
if (taxRemaining >= previousLocked) {
taxFromLocked = previousLocked;
taxRemaining -= previousLocked;
} else {
taxFromLocked = taxRemaining;
taxRemaining = 0;
}
taxAccrued += taxFromLocked;
} else {
taxFromLocked = 0;
}
if (taxFromLocked >= previousLocked) {
valuation = 0;
lockedValuation = 0;
baseValuation = 0;
taxPaidUntil = block.timestamp;
if (prepaidTaxBalance > 0) {
ownerRefund += prepaidTaxBalance;
prepaidTaxBalance = 0;
}
break;
}
uint256 newValuation = taxRemaining == 0
? theoreticalValuation
: previousValuation > taxFromLocked ? previousValuation - taxFromLocked : 0;
uint256 remainingLocked = previousLocked - taxFromLocked;
valuation = newValuation;
lockedValuation = remainingLocked;
taxPaidUntil += taxPeriodInSeconds;
if (dustThresholdValuation != 0 && valuation <= dustThresholdValuation) {
ownerRefund += remainingLocked;
valuation = 0;
lockedValuation = 0;
baseValuation = 0;
taxPaidUntil = block.timestamp;
if (prepaidTaxBalance > 0) {
ownerRefund += prepaidTaxBalance;
prepaidTaxBalance = 0;
}
break;
}
if (taxPaidUntil > block.timestamp) {
break;
}
if (valuation == 0 || lockedValuation == 0) {
ownerRefund += lockedValuation;
valuation = 0;
lockedValuation = 0;
baseValuation = 0;
taxPaidUntil = block.timestamp;
if (prepaidTaxBalance > 0) {
ownerRefund += prepaidTaxBalance;
prepaidTaxBalance = 0;
}
break;
}
}
if (taxAccrued > 0) {
_forwardToTreasury(taxAccrued);
}
if (ownerRefund > 0 && currentOwner != address(0)) {
_sendValue(payable(currentOwner), ownerRefund);
}
}
function _forwardToTreasury(uint256 amount) private {
emit TreasuryPayment(amount);
_sendValue(payable(treasury), amount);
}
function _sendValue(address payable to, uint256 amount) private {
emit FundsSent(to, amount);
(bool success, ) = to.call{value: amount}("");
if (!success) revert TransferFailed();
}
receive() external payable {
revert InvalidAmount();
}
fallback() external payable {
revert InvalidAmount();
}
}
"
},
"project/contracts/ValuationTaxShieldedSlot.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.28;
/**
* @title ValuationTaxShieldedSlot
* @notice Harberger-style slot where only a bonded fraction of the valuation is escrowed and refunded when coverage lapses.
*/
contract ValuationTaxShieldedSlot {
uint256 private constant RATE_DENOMINATOR = 1e4; // basis points (10000 = 100%)
uint256 private constant SECONDS_PER_YEAR = 365 days;
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
error Unauthorized();
error SlotOccupied();
error SlotVacant();
error InvalidAmount();
error InvalidParameter();
error ContentUpdateLimitReached();
error TaxStillActive();
error MinIncrementNotMet();
error TransferFailed();
error Reentrancy();
event SlotClaimed(address indexed newOwner, uint256 valuation, uint256 bondedAmount, uint256 paidThrough);
event SlotTakenOver(
address indexed previousOwner,
address indexed newOwner,
uint256 newValuation,
uint256 bondedAmount,
uint256 paidThrough
);
event SlotRenewed(address indexed owner, uint256 paidThrough);
event SlotForfeited(address indexed previousOwner);
event SlotReset(address indexed previousOwner);
event SlotExpired(address indexed previousOwner);
event SlotPoked(address indexed operator);
event AdCreativeUpdated(address indexed owner, string uri);
event TreasuryPayment(uint256 amount);
event FundsSent(address indexed to, uint256 amount);
address public immutable treasury;
address public immutable governance;
address public immutable factory;
uint256 public immutable bondRate;
uint256 public immutable contentUpdateLimit;
uint256 public immutable taxPeriodInSeconds;
uint256 public immutable annualTaxRate;
uint256 public immutable minBidIncrementRate;
uint256 public immutable minValuation;
address public currentOwner;
uint256 public valuation;
uint256 public bondedAmount;
uint256 public taxPaidUntil;
uint256 public prepaidTaxBalance;
uint256 public lastTaxSettlement;
uint256 public contentUpdateCount;
string public currentAdURI;
uint256 private _status;
struct SlotDetails {
address currentOwner;
uint256 valuation;
uint256 bondedAmount;
uint256 prepaidTaxBalance;
uint256 taxPaidUntil;
uint256 timeRemainingInSeconds;
bool isExpired;
uint256 contentUpdateCount;
uint256 contentUpdateLimit;
uint256 taxPeriodInSeconds;
uint256 annualTaxRate;
uint256 minBidIncrementRate;
uint256 bondRate;
uint256 minValuation;
string currentAdURI;
address treasury;
address governance;
bool isOccupied;
}
constructor(
address treasury_,
address governance_,
uint256 bondRate_,
uint256 contentUpdateLimit_,
uint256 taxPeriodInSeconds_,
uint256 annualTaxRate_,
uint256 minBidIncrementRate_,
uint256 minValuation_
) {
if (treasury_ == address(0) || governance_ == address(0)) revert InvalidParameter();
if (taxPeriodInSeconds_ == 0 || annualTaxRate_ == 0) revert InvalidParameter();
if (bondRate_ == 0 || bondRate_ > RATE_DENOMINATOR) revert InvalidParameter();
if (minValuation_ == 0) revert InvalidParameter();
treasury = treasury_;
governance = governance_;
factory = msg.sender;
bondRate = bondRate_;
contentUpdateLimit = contentUpdateLimit_;
taxPeriodInSeconds = taxPeriodInSeconds_;
annualTaxRate = annualTaxRate_;
minBidIncrementRate = minBidIncrementRate_;
minValuation = minValuation_;
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
if (_status == _ENTERED) revert Reentrancy();
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
modifier onlyOwner() {
if (msg.sender != currentOwner) revert Unauthorized();
_;
}
modifier onlyGovernance() {
if (msg.sender != governance) revert Unauthorized();
_;
}
function claim(uint256 newValuation, uint256 taxPeriods, string calldata newUri)
external
payable
nonReentrant
{
if (currentOwner != address(0)) revert SlotOccupied();
if (newValuation == 0 || taxPeriods == 0) revert InvalidParameter();
if (newValuation < minValuation) revert InvalidParameter();
uint256 bondRequired = _calculateBond(newValuation);
uint256 taxDue = _calculateTax(newValuation, taxPeriods);
if (taxDue == 0) revert InvalidParameter();
uint256 requiredValue = bondRequired + taxDue;
if (msg.value != requiredValue) revert InvalidAmount();
uint256 coverage = taxPeriodInSeconds * taxPeriods;
currentOwner = msg.sender;
valuation = newValuation;
bondedAmount = bondRequired;
taxPaidUntil = block.timestamp + coverage;
prepaidTaxBalance = taxDue;
lastTaxSettlement = block.timestamp;
contentUpdateCount = 0;
currentAdURI = newUri;
emit SlotClaimed(msg.sender, newValuation, bondRequired, taxPaidUntil);
}
function takeOver(uint256 newValuation, uint256 taxPeriods, string calldata newUri)
external
payable
nonReentrant
{
_expireIfNeeded();
address previousOwner = currentOwner;
if (previousOwner == address(0)) revert SlotVacant();
_settleAccruedTax();
uint256 refundableTax = prepaidTaxBalance;
prepaidTaxBalance = 0;
if (taxPeriods == 0 || newValuation == 0) revert InvalidParameter();
if (newValuation < minValuation) revert InvalidParameter();
uint256 minValuationRequired = valuation + ((valuation * minBidIncrementRate) / RATE_DENOMINATOR);
if (newValuation < minValuationRequired) revert MinIncrementNotMet();
uint256 bondRequired = _calculateBond(newValuation);
uint256 taxDue = _calculateTax(newValuation, taxPeriods);
if (taxDue == 0) revert InvalidParameter();
uint256 requiredValue = bondRequired + taxDue;
if (msg.value != requiredValue) revert InvalidAmount();
uint256 payout = bondedAmount;
uint256 coverage = taxPeriodInSeconds * taxPeriods;
uint256 paidThrough = block.timestamp + coverage;
currentOwner = msg.sender;
valuation = newValuation;
bondedAmount = bondRequired;
taxPaidUntil = paidThrough;
prepaidTaxBalance = taxDue;
lastTaxSettlement = block.timestamp;
contentUpdateCount = 0;
currentAdURI = newUri;
uint256 totalRefund = payout + refundableTax;
if (totalRefund > 0) {
_sendValue(payable(previousOwner), totalRefund);
}
emit SlotTakenOver(previousOwner, msg.sender, newValuation, bondRequired, paidThrough);
}
function renew(uint256 taxPeriods) external payable nonReentrant onlyOwner {
_expireIfNeeded();
if (currentOwner == address(0)) revert SlotVacant();
if (taxPeriods == 0) revert InvalidParameter();
_settleAccruedTax();
uint256 taxDue = _calculateTax(valuation, taxPeriods);
if (taxDue == 0) revert InvalidParameter();
if (msg.value != taxDue) revert InvalidAmount();
uint256 coverage = taxPeriodInSeconds * taxPeriods;
taxPaidUntil = taxPaidUntil + coverage;
prepaidTaxBalance += taxDue;
lastTaxSettlement = block.timestamp;
emit SlotRenewed(msg.sender, taxPaidUntil);
}
function forfeit() external nonReentrant onlyOwner {
_expireIfNeeded();
address previousOwner = currentOwner;
if (previousOwner == address(0)) revert SlotVacant();
if (block.timestamp >= taxPaidUntil) revert TaxStillActive();
_settleAccruedTax();
uint256 payout = bondedAmount;
uint256 refundableTax = prepaidTaxBalance;
prepaidTaxBalance = 0;
currentOwner = address(0);
valuation = 0;
bondedAmount = 0;
taxPaidUntil = 0;
lastTaxSettlement = 0;
contentUpdateCount = 0;
currentAdURI = "";
uint256 totalRefund = payout + refundableTax;
if (totalRefund > 0) {
_sendValue(payable(previousOwner), totalRefund);
}
emit SlotForfeited(previousOwner);
}
function governanceReset() external nonReentrant onlyGovernance {
_expireIfNeeded();
address previousOwner = currentOwner;
if (previousOwner == address(0)) revert SlotVacant();
_settleAccruedTax();
uint256 payout = bondedAmount;
uint256 refundableTax = prepaidTaxBalance;
prepaidTaxBalance = 0;
currentOwner = address(0);
valuation = 0;
bondedAmount = 0;
taxPaidUntil = 0;
lastTaxSettlement = 0;
contentUpdateCount = 0;
currentAdURI = "";
uint256 totalRefund = payout + refundableTax;
if (totalRefund > 0) {
_sendValue(payable(previousOwner), totalRefund);
}
emit SlotReset(previousOwner);
}
function poke() external nonReentrant {
if (currentOwner == address(0)) revert SlotVacant();
if (block.timestamp < taxPaidUntil) revert TaxStillActive();
_expireIfNeeded();
emit SlotPoked(msg.sender);
}
function updateAdCreative(string calldata newUri) external onlyOwner {
if (contentUpdateCount >= contentUpdateLimit) revert ContentUpdateLimitReached();
contentUpdateCount += 1;
currentAdURI = newUri;
emit AdCreativeUpdated(msg.sender, newUri);
}
function getSlotDetails() external view returns (SlotDetails memory details) {
bool expired = currentOwner != address(0) && block.timestamp >= taxPaidUntil;
uint256 timeRemaining = expired
? 0
: (taxPaidUntil > block.timestamp ? taxPaidUntil - block.timestamp : 0);
details = SlotDetails({
currentOwner: currentOwner,
valuation: valuation,
bondedAmount: bondedAmount,
prepaidTaxBalance: prepaidTaxBalance,
taxPaidUntil: taxPaidUntil,
timeRemainingInSeconds: timeRemaining,
isExpired: expired,
contentUpdateCount: contentUpdateCount,
contentUpdateLimit: contentUpdateLimit,
taxPeriodInSeconds: taxPeriodInSeconds,
annualTaxRate: annualTaxRate,
minBidIncrementRate: minBidIncrementRate,
bondRate: bondRate,
minValuation: minValuation,
currentAdURI: currentAdURI,
treasury: treasury,
governance: governance,
isOccupied: currentOwner != address(0)
});
}
function _expireIfNeeded() private {
if (currentOwner == address(0)) return;
if (block.timestamp < taxPaidUntil) return;
_settleAccruedTax();
if (prepaidTaxBalance > 0) {
uint256 remainingTax = prepaidTaxBalance;
prepaidTaxBalance = 0;
_forwardToTreasury(remainingTax);
}
address previousOwner = currentOwner;
uint256 payout = bondedAmount;
currentOwner = address(0);
valuation = 0;
bondedAmount = 0;
taxPaidUntil = 0;
lastTaxSettlement = 0;
contentUpdateCount = 0;
currentAdURI = "";
if (payout > 0) {
_sendValue(payable(previousOwner), payout);
}
emit SlotExpired(previousOwner);
}
function _settleAccruedTax() private {
if (currentOwner == address(0)) return;
uint256 settlementEnd = taxPaidUntil;
if (settlementEnd == 0) return;
uint256 effectiveTime = block.timestamp < settlementEnd ? block.timestamp : settlementEnd;
if (effectiveTime <= lastTaxSettlement) return;
uint256 totalRemaining = settlementEnd - lastTaxSettlement;
if (totalRemaining == 0) {
lastTaxSettlement = settlementEnd;
return;
}
uint256 elapsed = effectiveTime - lastTaxSettlement;
uint256 prepaid = prepaidTaxBalance;
if (prepaid == 0) {
lastTaxSettlement = effectiveTime;
return;
}
uint256 taxToForward = (prepaid * elapsed) / totalRemaining;
if (elapsed == totalRemaining || taxToForward > prepaid) {
taxToForward = prepaid;
}
prepaidTaxBalance = prepaid - taxToForward;
lastTaxSettlement = effectiveTime;
if (taxToForward > 0) {
_forwardToTreasury(taxToForward);
}
}
function _calculateTax(uint256 valuation_, uint256 periods) internal view returns (uint256) {
uint256 numerator = valuation_ * annualTaxRate * taxPeriodInSeconds * periods;
return numerator / (RATE_DENOMINATOR * SECONDS_PER_YEAR);
}
function _calculateBond(uint256 valuation_) internal view returns (uint256) {
return (valuation_ * bondRate) / RATE_DENOMINATOR;
}
function _forwardToTreasury(uint256 amount) private {
emit TreasuryPayment(amount);
_sendValue(payable(treasury), amount);
}
function _sendValue(address payable to, uint256 amount) private {
emit FundsSent(to, amount);
(bool success, ) = to.call{value: amount}("");
if (!success) revert TransferFailed();
}
receive() external payable {
revert InvalidAmount();
}
fallback() external payable {
revert InvalidAmount();
}
}
"
}
},
"settings": {
"evmVersion": "cancun",
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-11-04 18:03:23
Comments
Log in to comment.
No comments yet.