|
|
@@ -13,7 +13,8 @@ pub struct GasData {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-Total gas is their saturating sum, and the fee is `gas / 100`:
|
|
|
+Total gas is their saturating sum. The minimum fee for a transaction is
|
|
|
+`total_gas * FEE_PER_GAS`, and a mandatory fraction of it is burned:
|
|
|
|
|
|
```rust
|
|
|
pub fn total_gas_used(&self) -> u64 {
|
|
|
@@ -23,11 +24,106 @@ pub fn total_gas_used(&self) -> u64 {
|
|
|
.saturating_add(self.deployments)
|
|
|
}
|
|
|
|
|
|
-pub fn compute_fee(gas: &u64) -> u64 {
|
|
|
- gas / 100
|
|
|
+pub fn minimum_fee(gas: u64) -> FeeResult<u64> {
|
|
|
+ gas.checked_mul(FEE_PER_GAS).ok_or(FeeError::ArithmeticOverflow)
|
|
|
}
|
|
|
+
|
|
|
+pub fn burn_fee(minimum_fee: u64) -> FeeResult<u64> {
|
|
|
+ let burned =
|
|
|
+ (minimum_fee as u128).checked_mul(BURN_NUM as u128).ok_or(FeeError::ArithmeticOverflow)?
|
|
|
+ / BURN_DEN as u128;
|
|
|
+
|
|
|
+ u64::try_from(burned).map_err(|_| FeeError::ArithmeticOverflow)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+# Fee burning
|
|
|
+
|
|
|
+Fee pricing uses one fixed set of consensus constants. For a fee-paying
|
|
|
+transaction with final measured gas `g`, paid fee `paid`, and declared
|
|
|
+burned fee `burned`:
|
|
|
+
|
|
|
+```
|
|
|
+base_fee(g) = g * FEE_PER_GAS
|
|
|
+burn_fee(g) = floor(base_fee(g) * BURN_NUM / BURN_DEN)
|
|
|
+inclusion_fee(g) = base_fee(g) - burn_fee(g)
|
|
|
+tip_fee = paid - base_fee(g)
|
|
|
+```
|
|
|
+
|
|
|
+| Constant | Value | Description |
|
|
|
+|--------------|-------|-----------------------------------|
|
|
|
+| `FEE_PER_GAS`| 5 | Fee per gas unit |
|
|
|
+| `BURN_NUM` | 3 | Mandatory burn ratio numerator |
|
|
|
+| `BURN_DEN` | 4 | Mandatory burn ratio denominator |
|
|
|
+
|
|
|
+This sets the minimum fee to `5 * gas`, burns 75% of it before
|
|
|
+per-transaction rounding, and pays the remainder to the miner. Any
|
|
|
+payment above the minimum is an implicit miner tip; there is no
|
|
|
+separate priority-tip field.
|
|
|
+
|
|
|
+`paid` and `burned` are public values in the `Money::FeeV1` calldata:
|
|
|
+
|
|
|
+```
|
|
|
+[function_id][paid_fee][burned_fee][params]
|
|
|
+```
|
|
|
+
|
|
|
+The transaction is valid only if:
|
|
|
+
|
|
|
+```
|
|
|
+paid >= base_fee(g)
|
|
|
+burned >= burn_fee(base_fee(g))
|
|
|
+burned <= paid
|
|
|
```
|
|
|
|
|
|
+The burn is enforced as a mandatory floor rather than an exact value.
|
|
|
+Builders cannot predict final gas exactly, since the fee call's own gas
|
|
|
+usage depends on chain state (tree depths) and varies slightly between
|
|
|
+builds of the same transaction, so they estimate conservatively with
|
|
|
+`FEE_CALL_GAS` as an upper bound. Under-declaring the burn below the
|
|
|
+mandatory amount is invalid. Over-declaring is valid and only burns
|
|
|
+more of the builder's own value, reducing the miner-claimable amount;
|
|
|
+no value can be forged.
|
|
|
+
|
|
|
+Overpayment is settled against the fee values the transaction declares,
|
|
|
+not against what the chain actually required. The miner always receives
|
|
|
+`paid - burned`:
|
|
|
+
|
|
|
+* A deliberate tip above the builder's own estimated minimum raises
|
|
|
+ `paid` only, so it goes entirely to the miner.
|
|
|
+* Estimation error raises `paid` and the declared `burned` together,
|
|
|
+ since the burn is declared from the estimate while the floor is
|
|
|
+ enforced from the actual requirement. Over-estimated gas therefore
|
|
|
+ splits the slack: 75% is burned and 25% accrues to the miner's
|
|
|
+ inclusion fee. Under-estimation makes the transaction invalid.
|
|
|
+
|
|
|
+| Extra payment from… | Burned | Miner |
|
|
|
+|------------------------------------|--------|-------|
|
|
|
+| a deliberate tip over the estimate | 0% | 100% |
|
|
|
+| estimation slack over actual gas | 75% | 25% |
|
|
|
+
|
|
|
+Fee interpretation is split between the contract and the validator:
|
|
|
+the contract handles public numbers and money accounting, while the
|
|
|
+validator binds those numbers to runtime facts. `Money::FeeV1` checks
|
|
|
+the native-token fee spend and value conservation
|
|
|
+(`input_value = output_value + paid`, `paid > 0`, `burned <= paid`) and
|
|
|
+owns the height fee accumulator:
|
|
|
+
|
|
|
+```
|
|
|
+fees[h] = checked_add(fees[h], paid - burned)
|
|
|
+```
|
|
|
+
|
|
|
+There is no separate burn accumulator. Burning is represented by the
|
|
|
+fee spend not being fully re-minted: the `PoWRewardV1` transaction
|
|
|
+mints exactly `expected_reward(h) + fees[h]`, and the burned amount is
|
|
|
+absent from it.
|
|
|
+
|
|
|
+After final gas is known, validator verification requires exactly one
|
|
|
+well-formed `Money::FeeV1` call, checks the validity conditions above,
|
|
|
+and rejects malformed fee calldata fallibly instead of panicking. All
|
|
|
+fee arithmetic uses checked intermediates; overflow makes the
|
|
|
+transaction or block invalid. Fee verification can be disabled only as
|
|
|
+an explicit test/development mode.
|
|
|
+
|
|
|
# Fee metering
|
|
|
|
|
|
## WASM opcodes
|
|
|
@@ -157,7 +253,7 @@ gas which includes the fee call itself. It is therefore covered by a
|
|
|
fixed constant added to the base gas:
|
|
|
|
|
|
```
|
|
|
-fee = (base_tx_gas + FEE_CALL_GAS) / 100
|
|
|
+fee = minimum_fee(base_tx_gas + FEE_CALL_GAS)
|
|
|
```
|
|
|
|
|
|
`FEE_CALL_GAS = 42_000_000` is intentionally conservative. The actual
|
|
|
@@ -169,7 +265,10 @@ This data-dependent variation (~+/-70K gas across runs) means no single
|
|
|
constant can be exact. The 42M value provides ~24% headroom over the
|
|
|
observed maximum, ensuring the estimate always covers the real overhead
|
|
|
without requiring per-transaction recalibration or risking intermittent
|
|
|
-fee-shortfall failures.
|
|
|
+fee-shortfall failures. Since the mandatory burn is enforced as a
|
|
|
+floor, over-estimation keeps transactions valid; if the fee call ever
|
|
|
+outgrows the constant, fee verification rejects transactions loudly and
|
|
|
+the constant has to be bumped.
|
|
|
|
|
|
# Gas limits
|
|
|
|
|
|
@@ -213,8 +312,8 @@ operation. Gas scales linearly with inputs (~4.35M each) and outputs
|
|
|
|
|
|
# TODO
|
|
|
|
|
|
-* Implement fee burning contracts and algorithm.
|
|
|
-* Implement the gas to DRK conversion algorithm + priority tip.
|
|
|
+* Revisit adaptive fee pricing or fee periods (explicit priority tips
|
|
|
+ were dropped in favor of implicit tips from overpayment).
|
|
|
* Re-derive fee constants on target validator architecture. The current
|
|
|
ratios are calibrated on a single x86-64 machine and are approximate.
|
|
|
* Finalize security budget (fees in the context of block rewards).
|