Skip to content

feat: introduce a protocol fee margin (AZIP-23) - #25260

Draft
aminsammara wants to merge 1 commit into
nextfrom
as/azip-23-protocol-fee-margin
Draft

feat: introduce a protocol fee margin (AZIP-23)#25260
aminsammara wants to merge 1 commit into
nextfrom
as/azip-23-protocol-fee-margin

Conversation

@aminsammara

Copy link
Copy Markdown
Contributor

Adds a governance-set margin mu on the mana base fee. The fee becomes cost * (1 + mu) * congestionMultiplier: operators still receive exactly cost from the fee waterfall plus the unchanged block inflation, and the markup above cost goes to a governance-set protocolFeeRecipient, which defaults to the current burn address.

  • mu is a uint16 bps field packed into bits 224-239 of CompressedFeeConfig and deploys at 0, so the rollup is bit-identical to today.
  • setProtocolFeeMargin is governance-only and rate-limited to x3/2 on the fee multiplier per 30-day window, matching setProvingCostPerMana. Decreases are immediate and unrestricted; setting the current value is a no-op.
  • The (1 + mu) scaling is applied only to the fakeExponential factor in congestionMultiplier. The mulDiv divisor stays MINIMUM_CONGESTION_MULTIPLIER -- scaling both sites cancels the margin.
  • The fee header's uint64 congestionCost field becomes protocolFee and carries summedMinFee - sequencerCost - proverCost as a single subtraction clamped at 0, so fee - protocolFee = cost * manaUsed holds to the wei.
  • RewardLib.BURN_ADDRESS becomes the deploy-time default for a stored protocolFeeRecipient; getBurnAddress() is replaced by getProtocolFeeRecipient().
  • The TypeScript fee mirror is updated to match bit-exactly, with mu threaded through the L1 config read into the fee predictor.

Adds a governance-set margin `mu` on the mana base fee. The fee becomes
`cost * (1 + mu) * congestionMultiplier`: operators still receive exactly
`cost` from the fee waterfall plus the unchanged block reward, and the markup
above cost goes to a governance-set `protocolFeeRecipient`, which defaults to
the current burn address.

- `mu` is a uint16 bps field packed into bits 224-239 of `CompressedFeeConfig`
  and deploys at 0, so the rollup is bit-identical to today.
- `setProtocolFeeMargin` is governance-only and rate-limited to x3/2 on the fee
  multiplier per 30-day window, matching `setProvingCostPerMana`. Decreases are
  immediate and unrestricted; setting the current value is a no-op.
- The `(1 + mu)` scaling is applied only to the `fakeExponential` factor in
  `congestionMultiplier`. The `mulDiv` divisor stays
  `MINIMUM_CONGESTION_MULTIPLIER` -- scaling both sites cancels the margin.
- The fee header's uint64 `congestionCost` field becomes `protocolFee` and
  carries `summedMinFee - sequencerCost - proverCost` as a single subtraction
  clamped at 0, so `fee - protocolFee = cost * manaUsed` holds to the wei.
- `RewardLib.BURN_ADDRESS` becomes the deploy-time default for a stored
  `protocolFeeRecipient`; `getBurnAddress()` is replaced by
  `getProtocolFeeRecipient()`.
- The TypeScript fee mirror is updated to match bit-exactly, with `mu` threaded
  through the L1 config read into the fee predictor.
event ManaTargetUpdated(uint256 indexed manaTarget);
event PrunedPending(uint256 provenCheckpointNumber, uint256 pendingCheckpointNumber);
event ProtocolFeeMarginUpdated(uint16 oldBps, uint16 newBps);
event ProtocolFeeRecipientUpdated(address indexed oldRecipient, address indexed newRecipient);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we need indexed here.

uint256 manaTarget;
uint256 congestionUpdateFraction;
EthValue provingCostPerMana;
uint256 protocolFeeMarginBps;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be uint16?

}

function getProtocolFeeMarginBps(CompressedFeeConfig _compressedFeeConfig) internal pure returns (uint256) {
return (CompressedFeeConfig.unwrap(_compressedFeeConfig) >> 224) & MASK_16_BITS;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's look at this carefully

uint1 preHeat;
uint63 proverCost; Max value: 9.2233720369E18
uint64 congestionCost;
uint64 protocolFee;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's verify that this is both the congestionCost + protocolFeeMargin

excessMana: uint256(excessMana),
ethPerFeeAsset: uint256(ethPerFeeAsset),
congestionCost: uint256(congestionCost),
protocolFee: uint256(protocolFee),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is supposed to be \mu(sequencerCostPerMana + proverCostPerMana) + congestionCost

RewardLib.updateConfig(_config);
}

function updateProtocolFeeMargin(uint16 _bps) external returns (bool changed, uint16 oldBps) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be gated so that callable by governance only. Here or in the FeeLib.sol

return FeeLib.updateProtocolFeeMargin(_bps);
}

function updateProtocolFeeRecipient(address _recipient) external returns (address oldRecipient) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be gated so that callable by governance only. Here or in RewardLib.sol

// such as sacrificial hearts, during rituals performed within temples.
// Deploy-time default for `protocolFeeRecipient`: the protocol fee is burned until governance
// redirects it via {updateProtocolFeeRecipient}.
address public constant BURN_ADDRESS = address(bytes20("CUAUHXICALLI"));

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we rename the BURN_ADDRESS constant?
Or we can remove this constant altogether and just set protocolFeeRecipient to address(bytes20("CUAUHXICALLI")) in initializeConfig

/// @notice Owner-gated post-deployment writer for the protocol fee recipient.
/// @param _recipient The new recipient of the protocol fee tranche
/// @return oldRecipient The recipient in effect before this call
function updateProtocolFeeRecipient(address _recipient) internal returns (address oldRecipient) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the event is not emitted from here, where is it emitted from? should it be emitted from here instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay it's emitted in the next file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant