> ## Documentation Index
> Fetch the complete documentation index at: https://docs.celo.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Adding Fee Currencies

> How to implement and register a new ERC20 token as a fee currency on Celo

Any ERC20 token can become a fee currency on Celo. Two steps are required:

1. **Implement the `IFeeCurrency` interface** — the token must extend ERC20 with the functions the Celo blockchain uses to debit and credit gas fees. This step is the responsibility of the token issuer.
2. **Register the token through governance** — the token must be added to the on-chain allowlist via a governance proposal. The Celo team can support you through this step.

This guide walks through both steps.

For background on how fee abstraction works, see the [Overview](/build/fee-abstraction/overview).

***

## Step 1: Implement the IFeeCurrency Interface

This step must be completed by the token issuer — every project implements and maintains its own fee currency token.

### The IFeeCurrency Interface

Fee currencies must implement the [IFeeCurrency](https://github.com/celo-org/fee-currency-example/blob/92e2fcb/src/IFeeCurrency.sol) interface, which extends ERC20 with two additional functions used by the Celo blockchain to debit and credit gas fees.

When a [CIP-64](https://github.com/celo-org/celo-proposals/blob/master/CIPs/cip-0064.md) transaction is executed:

1. **Before execution** — the blockchain calls `debitGasFees` to reserve the maximum gas the transaction can spend
2. **After execution** — the blockchain calls `creditGasFees` to refund unused gas and distribute fees to the appropriate recipients

#### debitGasFees

```solidity theme={null}
function debitGasFees(address from, uint256 value) external;
```

Called before transaction execution to reserve the maximum gas amount.

* Must deduct `value` from `from`'s balance
* Must revert if `msg.sender` is not `address(0)` (only the VM may call this)

#### creditGasFees

There are two versions of `creditGasFees`. Both should be implemented for compatibility.

**New signature** (used once all fee currencies have migrated):

```solidity theme={null}
function creditGasFees(address[] calldata recipients, uint256[] calldata amounts) external;
```

* Must credit each `recipient` the corresponding `amount`
* Must revert if `msg.sender` is not `address(0)`
* Must revert if `recipients` and `amounts` have different lengths

**Legacy signature** (for backwards compatibility):

```solidity theme={null}
function creditGasFees(
    address refundRecipient,
    address tipRecipient,
    address _gatewayFeeRecipient,
    address baseFeeRecipient,
    uint256 refundAmount,
    uint256 tipAmount,
    uint256 _gatewayFeeAmount,
    uint256 baseFeeAmount
) external;
```

* `_gatewayFeeRecipient` and `_gatewayFeeAmount` are deprecated and will always be zero
* Must revert if `msg.sender` is not `address(0)`

***

### Example Implementation

The following example from [celo-org/fee-currency-example](https://github.com/celo-org/fee-currency-example) shows a minimal fee currency token using OpenZeppelin's ERC20 with burn/mint mechanics for gas fee handling:

```solidity theme={null}
pragma solidity ^0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IFeeCurrency} from "./IFeeCurrency.sol";

contract FeeCurrency is ERC20, IFeeCurrency {
    constructor(uint256 initialSupply) ERC20("ExampleFeeCurrency", "EFC") {
        _mint(msg.sender, initialSupply);
    }

    modifier onlyVm() {
        require(msg.sender == address(0), "Only VM can call");
        _;
    }

    function debitGasFees(address from, uint256 value) external onlyVm {
        _burn(from, value);
    }

    // New function signature
    function creditGasFees(
        address[] calldata recipients,
        uint256[] calldata amounts
    ) public onlyVm {
        require(
            recipients.length == amounts.length,
            "Recipients and amounts must be the same length."
        );

        for (uint256 i = 0; i < recipients.length; i++) {
            _mint(recipients[i], amounts[i]);
        }
    }

    // Legacy function signature for backwards compatibility
    function creditGasFees(
        address from,
        address feeRecipient,
        address, // gatewayFeeRecipient, unused
        address communityFund,
        uint256 refund,
        uint256 tipTxFee,
        uint256, // gatewayFee, unused
        uint256 baseTxFee
    ) public onlyVm {
        _mint(from, refund);
        _mint(feeRecipient, tipTxFee);
        _mint(communityFund, baseTxFee);
    }
}
```

This implementation uses `_burn` in `debitGasFees` and `_mint` in `creditGasFees` to handle the gas fee lifecycle. The `onlyVm` modifier ensures only the blockchain itself (via `address(0)`) can call these functions.

***

### Testing

Use [Foundry](https://book.getfoundry.sh/) to test your fee currency implementation. The [fee-currency-example](https://github.com/celo-org/fee-currency-example) repository includes a test suite you can use as a starting point:

```bash theme={null}
git clone https://github.com/celo-org/fee-currency-example.git
cd fee-currency-example
forge build
forge test
```

Key things to test:

* `debitGasFees` correctly reduces the sender's balance
* `debitGasFees` reverts when called by any address other than `address(0)`
* Both `creditGasFees` signatures correctly credit all recipients
* `creditGasFees` reverts when called by any address other than `address(0)`
* The total debited amount equals the total credited amount across a transaction lifecycle

***

## Step 2: Register Through Governance

Once your token implements `IFeeCurrency`, it must be added to the on-chain allowlist in [**FeeCurrencyDirectory.sol**](/build/tools/contracts/core-contracts) through a governance proposal. The governance process ensures that fee currencies meet the necessary requirements for network stability.

Unlike step 1, you don't have to navigate this step alone — the Celo team can support you through the governance process. Reach out on the [Celo Discord server](https://discord.com/invite/celo) to get started.

If your token uses decimals other than 18, you will also need to deploy an adapter contract. See [Adapters for Non-18-Decimal Tokens](/build/fee-abstraction/using-fee-abstraction#adapters-for-non-18-decimal-tokens) for details.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.