Skip to content

DDMinter

Replaceable economics policy. Computes DD issuance from (amount, maturity, now) and mints DD directly via DD's mint.

contracts/src/platforms/venice/DDMinter.sol

Inherits

Ownable2Step

Constants

NameValue
ROLE_DELAY7 days

Immutables

NameType
ddDD

Mutable state

NamePurpose
lockerSole authorized caller of issue()
pendingLocker, lockerEffectiveAtRotation buffer
minLockAmountMinimum amount per lock (e.g. 0.01 DIEM)
maxLockHorizonMaximum seconds-to-maturity (e.g. 400 days)

Wiring setters

solidity
function setLockerInitial(address locker_) external onlyOwner;
function proposeLocker(address locker_) external onlyOwner;
function activateLocker() external;
function cancelLockerChange() external onlyOwner;

No cross-check inside DDMinter — Locker.ddMinter is mutable, so chicken-and-egg. Cross-check lives on the Locker side in Locker.proposeDdMinter.

Config

solidity
function setConfig(uint256 minLockAmount_, uint64 maxLockHorizon_) external onlyOwner;

Instant (no timelock). Small surface: only affects future locks, never existing positions.

Issuance

solidity
function quote(uint256 amount, uint64 maturity, uint256 timestamp)
    public view
    returns (uint256 ddMinted);

function issue(address recipient, uint256 amount, uint64 maturity)
    external onlyLocker
    returns (uint256 ddMinted);

quote formula:

require amount ≥ minLockAmount
require maturity > timestamp
require maturity ≤ timestamp + maxLockHorizon

seconds = maturity - timestamp
days    = seconds / 86400        (floor)
if days == 0 then days = 1       (minimum 1 day)
ddMinted = amount × days

issue wraps quote and immediately dd.mint(recipient, ddMinted).

Events

LockerProposed, LockerActivated, LockerChangeCancelled,
ConfigSet

Errors

ZeroAddress, NotLocker, ZeroAmount, AmountBelowMinimum,
MaturityInPast, MaturityTooFar,
AlreadySet, NoPending, TooEarly

Why floor + min 1 day

Earlier ceiling-rounding formula let attackers lock 1 second before a UTC day boundary to get 2 × amount DD instead of 1 × amount. With floor + minimum-1-day:

  • secondsToMaturity == 1: 1 day issued (minimum).
  • secondsToMaturity == 86399: 1 day issued.
  • secondsToMaturity == 86400: 1 day issued.
  • secondsToMaturity == 86401: 1 day issued.
  • secondsToMaturity == 172800: 2 days issued.

No way to game timestamp positioning for extra DD.

Replaceability

DDMinter is a policy module. Future versions can introduce non-linear issuance (longer-lock bonus tiers, dynamic discounting, anti-MEV adjustments) by:

  1. Deploying DDMinter v2 with new logic.
  2. Calling dd.proposeMinterAdd(v2) → wait 7d → activateMinterAdd(v2).
  3. Calling v2.setLockerInitial(currentLocker).
  4. Calling locker.proposeDdMinter(v2) → wait 7d → activateDdMinter().
  5. Optionally calling dd.proposeMinterRemove(v1) → wait 7d → activate.

The full transition takes ~3 weeks (two 7-day timelocks back to back, plus buffer). During the transition, both DDMinters are minters of DD; only one is wired into Locker at a time.

Released under the MIT License.