Skip to main content
This page documents StableNet’s WEMIX-style fee delegation mechanism.
Fee delegation allows a separate account (FeePayer) to pay the transaction gas costs, while the transaction sender (From) only provides the value transfer.
This feature enables use cases where application operators subsidize transaction fees for users.
For gas fee policies and tip requirements, refer to Gas Fee Policy.
For the general transaction processing flow, refer to Transaction Lifecycle.

Purpose and Scope

Fee delegation clearly separates the financial responsibilities of a transaction between two parties.
  • Sender (From)
    Signs the transaction intent, provides the Value to be transferred, and must hold a valid nonce.
  • Fee Payer (FeePayer)
    Separately signs to approve gas payment and must hold sufficient balance to cover gas costs.
This mechanism is implemented via a dedicated transaction type, FeeDelegateDynamicFeeTxType = 0x16, and requires the Applepie fork to be active.

Transaction Type and Structure

Transaction Type Definition

Fee delegation uses transaction type 0x16 (decimal 22).
ConstantValueDescription
FeeDelegateDynamicFeeTxType0x16Fee-delegated EIP-1559 style transaction

Core Data Structures

The Message structure includes fee delegation fields.
Message {
  From common.Address       // Transaction sender (signs transaction intent)
  To *common.Address        // Recipient
  Value *big.Int            // Value to transfer from sender
  GasLimit uint64
  GasPrice *big.Int
  GasFeeCap *big.Int
  GasTipCap *big.Int
  FeePayer *common.Address  // Account paying for gas (optional)
  ...
}
The TxData interface includes fee delegation–related methods.
type TxData interface {
  feePayer() *common.Address
  rawFeePayerSignatureValues() (v, r, s *big.Int)
  ...
}

Dual Signature Requirement

Fee-delegated transactions require two independent signatures.

Signature 1: Transaction Sender

  • Signs the transaction data (to, value, data, gas parameters, nonce).
  • Signature values: V, R, S
  • Proves transaction intent and authorization.

Signature 2: Fee Payer

  • Signs consent to pay gas for the transaction.
  • Signature values: FV, FR, FS
  • Retrieved via the rawFeePayerSignatureValues() method.
This dual-signature structure guarantees that both the sender and the fee payer explicitly agree to their respective responsibilities.

State Transition Process

Transaction-to-Message Conversion

A fee-delegated transaction is converted into a Message upon entering the state transition.
FeeDelegateDynamicFeeTx            Message
+--------------------------+       +---------------------------+
| SenderTx.Nonce           | ----> | Nonce                     |
| SenderTx.GasFeeCap       | ----> | GasFeeCap                 |
| SenderTx.GasTipCap       | ----> | GasTipCap                 |
| SenderTx.Gas             | ----> | GasLimit                  |
| SenderTx.To              | ----> | To                        |
| SenderTx.Value           | ----> | Value                     |
| SenderTx.Data            | ----> | Data                      |
| SenderTx.AccessList      | ----> | AccessList                |
| FeePayer                 | ----> | FeePayer                  |
+--------------------------+       +---------------------------+
                                   | From = recovered sender   |
                                   | GasPrice = effectivePrice |
                                   +---------------------------+
TransactionToMessage recovers the From address from the sender’s signature (V/R/S) and sets the FeePayer field via tx.FeePayer().
The fee payer’s signature is used only to authorize gas payment and is not used to derive the From address.

Gas Purchase (buyGas)

When fee delegation is active, buyGas performs separated balance checks and deductions.

Balance Validation Rules

ScenarioFeePayer Balance CheckSender Balance Check
Fee Delegation ActivegasLimit × gasPrice + blob feesvalue
Standard TransactionN/AgasLimit × gasPrice + value + blob fees

EVM Execution Context

Fee delegation does not alter EVM execution semantics.
  • tx.origin: Always the transaction sender (From)
  • Top-level caller: Transaction sender
  • FeePayer: Not exposed to the EVM context
As a result, smart contracts cannot directly distinguish whether fee delegation was used.

Gas Refund Process

After execution, unused gas is refunded to the account that actually paid for gas.
func (st *StateTransition) refundGas(refundQuotient uint64) uint64 {
    refund := st.gasUsed() / refundQuotient
    if refund > st.state.GetRefund() {
        refund = st.state.GetRefund()
    }
    st.gasRemaining += refund
    remaining := uint256.NewInt(st.gasRemaining)
    remaining = remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
    if st.msg.FeePayer != nil {
        st.state.AddBalance(*st.msg.FeePayer, remaining)
    } else {
        st.state.AddBalance(st.msg.From, remaining)
    }
    st.gp.AddGas(st.gasRemaining)
    return refund
}

Fee Payment to Coinbase

Validators receive fees based on actual gas usage.
Fee payment is processed in the later stages of state transition and includes blacklist checks for the fee payer.

Fork Activation Requirement

Fee delegation is only permitted after the Applepie fork.
if isFeeDelegation && !st.evm.ChainConfig().IsApplepie(st.evm.Context.BlockNumber) {
    return ErrTxTypeNotSupported
}
For blocks prior to Applepie, fee-delegated transactions are immediately rejected during the preCheck stage.

Transaction Construction via RPC

TransactionArgs Fields

FieldTypePurpose
From*common.AddressTransaction sender
FeePayer*common.AddressOptional fee payer
V,R,S*hexutil.BigSender signature
FV,FR,FSraw tx onlyFeePayer signature

JSON Representation

{
  "type": "0x16",
  "chainId": "0x2052",
  "nonce": "0x1",
  "to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
  "gas": "0x76c0",
  "maxFeePerGas": "0x9184e72a000",
  "maxPriorityFeePerGas": "0x5f5e100",
  "value": "0xde0b6b3a7640000",
  "input": "0x",
  "accessList": [],
  "v": "0x0",
  "r": "0x...",
  "s": "0x...",
  "feePayer": "0x407d73d8a49eeb85d32cf465507dd71d507100c1",
  "fv": "0x0",
  "fr": "0x...",
  "fs": "0x..."
}

Key Invariants

  1. Balance Isolation
    The fee payer covers gas only, while the sender covers value only.
  2. Origin Preservation
    tx.origin is always the sender.
  3. Dual Consent
    Both accounts must sign for the transaction to be valid.
  4. Fork Gate
    Available only after Applepie activation.
  5. Refund Consistency
    Unused gas is refunded to the actual payer.