-
Notifications
You must be signed in to change notification settings - Fork 812
ERC-8054: Forkable [ERC-20](./eip-20.md) Token #1271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kevzzsk
wants to merge
6
commits into
ethereum:master
Choose a base branch
from
kevzzsk:kevin/forkable-erc20-token
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+257
−0
Open
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
856a968
Add ERC: Forkable ERC-20 Token
kevzzsk 257d2ad
fix lint
kevzzsk a11ab78
chore: update erc number
kevzzsk 4822744
chore: fix template
kevzzsk 49177d2
chore: update interface
kevzzsk 8035f46
chore: add reference implementation
kevzzsk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,257 @@ | ||
| --- | ||
| eip: 8054 | ||
| title: Forkable [ERC-20](./eip-20.md) Token | ||
| description: Checkpoint-based ERC-20 tokens that can be efficiently forked to distribute new tokens to existing holders. | ||
| author: Kevin (@kevzzsk), Fuxing (@fuxingloh) | ||
| discussions-to: https://ethereum-magicians.org/t/erc-8054-forkable-erc-20-token/25853 | ||
| status: Draft | ||
| type: Standards Track | ||
| category: ERC | ||
| created: 2025-10-10 | ||
| requires: 20 | ||
| --- | ||
|
|
||
| ## Abstract | ||
|
|
||
| This standard extends [ERC-20](./eip-20.md) to enable efficient token forking through checkpoint-based balance tracking. | ||
| A forkable ERC-20 token records balance snapshots at each state change. | ||
| A forked ERC-20 token inherits all balances from a specific checkpoint in the source token, | ||
| allowing instant, gas-free distribution to holders without airdrops or claiming mechanisms. | ||
|
|
||
| ## Motivation | ||
|
|
||
| Current methods for distributing new tokens based on existing ERC-20 token balances are inefficient: | ||
| 1. **Manual airdrops**: Taking snapshots and transferring tokens to each holder individually is expensive and gas-intensive. | ||
| 2. **Merkle-based claims**: While cheaper for deployers, | ||
| this approach requires users to pay gas fees to claim tokens and provides poor UX due to claimer needing to provide proofs. | ||
|
|
||
| Both approaches are inefficient as they rely on off-chain data construction outside the protocol's trust domain, | ||
| introducing potential inconsistencies and allowing for collusion within the Merkle structure. | ||
|
|
||
| This EIP proposes a standard for forkable ERC-20 tokens that: | ||
| - Enable zero-gas distribution to token holders | ||
| - Eliminate manual claiming processes | ||
| - Provide verifiable on-chain balance inheritance | ||
| - Maintain full ERC-20 compatibility | ||
|
|
||
| By implementing checkpointed balances, tokens can be efficiently forked at any historical point, | ||
| with new token balances automatically derived from the source token without any state duplication or expensive operations. | ||
|
|
||
| ### Use Cases | ||
|
|
||
| #### Airdrops | ||
|
|
||
| Airdrops are a common use case for forkable tokens. | ||
| Without forkable ERC-20 tokens, manual snapshotting and merkle root creation are required. | ||
| Then users must manually claim the new ERC-20 token costing gas borne by the claimer. | ||
|
|
||
| With forkable ERC-20 tokens, users do not have to claim the new ERC-20 token. | ||
| The forked ERC-20 token is automatically transferred (via inheritance) | ||
| to the users who have positive balance at the fork point. | ||
|
|
||
| #### Tokenized Risk and Yield | ||
|
|
||
| [ERC-4626](./eip-4626.md) is a popular standard for yield-bearing vaults that manage an underlying ERC-20 asset. | ||
| Risk and yield are commonly rebased on the same underlying asset, | ||
| and this works very well for single-dimensional yield and risk | ||
| (Liquid PoS ETH). | ||
|
|
||
| However, for multidimensional yield and risk vaults, | ||
| the underlying asset may be used for different yield-generating purposes each with their own risk profile. | ||
| Forkable ERC-20 tokens allow for tokenization of risk and yield to its immediate beneficiaries. | ||
| While this is not a foreign concept in the space, | ||
| its implementation has so far been off-chain—with their own trust domain separate from the chain. | ||
|
|
||
| #### Token Migration | ||
|
|
||
| Protocol upgrades, tokenomics changes, or contract improvements often require migrating to a new token. | ||
|
|
||
| Without forkable ERC-20 tokens, migration requires complex processes: | ||
| - Taking manual snapshots of all holder balances | ||
| - Deploying the new token contract | ||
| - Either airdropping to all holders (expensive) or requiring users to manually claim their tokens via merkle proofs | ||
| (poor UX) | ||
|
|
||
| With forkable ERC-20 tokens, migration becomes seamless: | ||
| - The new token is forked from the old token at a specific checkpoint | ||
| - All holder balances are automatically inherited from the checkpoint | ||
| - Users can immediately interact with the new token without claiming | ||
| - No gas costs for holders, no manual snapshot management required | ||
|
|
||
| #### Governance Token Derivatives | ||
|
|
||
| Create governance tokens or voting power derivatives based on historical token holdings without affecting the original token's utility or requiring users to lock or migrate their holdings. | ||
|
|
||
| #### Rewards and Loyalty Programs | ||
|
|
||
| Distribute loyalty or reward tokens proportional to historical holdings or activity, | ||
| tracked via checkpoints, without complex off-chain calculation and distribution logic. | ||
|
|
||
| ## Specification | ||
|
|
||
| The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", | ||
| "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. | ||
|
|
||
| ### Forkable ERC-20 tokens (Source Token) | ||
|
|
||
| Forkable ERC-20 tokens are ERC-20 compliant tokens that have their balances and total supply saved at every state-changing operation. | ||
| Each checkpoint is associated with a monotonically increasing nonce. | ||
|
|
||
| All forkable ERC-20 tokens: | ||
| - MUST implement ERC-20 | ||
| - MUST implement optional ERC-20 metadata that includes: | ||
| - name (string) | ||
| - symbol (string) | ||
| - decimals (uint8) | ||
|
|
||
| #### interface | ||
|
|
||
| ```solidity | ||
| interface IERC20Checkpointed is IERC20, IERC20Metadata { | ||
| /** | ||
| * @dev Returns the value of tokens in existence at specified checkpoint. | ||
| * @param checkpoint The checkpoint to get the total supply at. | ||
| */ | ||
| function totalSupplyAt(uint48 checkpoint) external view returns (uint256); | ||
|
|
||
| /** | ||
| * @dev Returns the amount of tokens owned by `account` at specified checkpoint. | ||
| * @param account The account to get the balance of. | ||
| * @param checkpoint The checkpoint to get the balance at. | ||
| */ | ||
| function balanceOfAt(address account, uint48 checkpoint) external view returns (uint256); | ||
|
|
||
| /** | ||
| * @dev Returns the current checkpoint nonce. | ||
| */ | ||
| function checkpointNonce() external view returns (uint48); | ||
| } | ||
| ``` | ||
|
|
||
| #### Behavior Specifications | ||
|
|
||
| - `totalSupplyAt(checkpoint)` MUST return the total supply of tokens at the specified checkpoint. | ||
| - `balanceOfAt(account, checkpoint)` MUST return the balance of tokens owned by `account` at the specified checkpoint. | ||
| - `totalSupply()` MUST return the latest checkpointed total supply of tokens. | ||
| - `balanceOf(account)` MUST return the latest checkpointed balance of token held by the account. | ||
| - Any state changes (transfer, mint, burn) MUST push the latest checkpoint balances and total supply. | ||
| - Checkpoint nonces MUST be monotonically increasing. | ||
| - Querying a future checkpoint MUST return the latest known value. | ||
|
|
||
| ### Forked ERC-20 tokens | ||
|
|
||
| Forked ERC-20 tokens are ERC-20 compliant tokens that are forked from a checkpointed token at a specific checkpoint nonce. | ||
| They inherit all balances from that checkpoint. | ||
|
|
||
| All forked ERC-20 tokens: | ||
|
|
||
| - MUST implement ERC-20 | ||
| - MUST implement optional ERC-20 metadata that includes: | ||
| - `name` (string) | ||
| - `symbol` (string) | ||
| - `decimals` (uint8) | ||
| - MUST take as constructor (or initializer) inputs: | ||
| - `name` (string) - The name of the forked token | ||
| - `symbol` (string) - The symbol of the forked token | ||
| - `checkpointedNonce` (uint48) - The checkpoint at which to fork | ||
| - `checkpointedToken` (address) - The address of the source token implementing `IERC20Checkpointed` | ||
| - MUST set `decimals` equal to the source token’s `decimals`. | ||
| - checkpointed nonce supplied for the fork MUST be in the past or equal to the most recent checkpoint nonce. | ||
| - Initial `totalSupply` MUST equal `IERC20Checkpointed(totalSupplyAt(checkpointedNonce))`. | ||
| - For any account A, the forked token’s initial balance MUST equal | ||
| `IERC20Checkpointed(balanceOfAt(A, checkpointedNonce))`. | ||
| - MAY implement `IERC20Checkpointed`, enabling recursive forking. | ||
| - Allowances and nonces are NOT carried over, | ||
| integrators should treat the fork as a fresh ERC-20 for approvals and permits. | ||
| - If the source token does not implement `IERC20Checkpointed`, the fork mechanism is out-of-scope of this standard. | ||
|
|
||
| #### Behavior Specifications | ||
|
|
||
| - `balanceOf(account)` MUST return `checkpointedToken.balanceOfAt(account, checkpointedNonce)` if no state changes have occurred for that account in the forked token since its creation. | ||
| - `balanceOf(account)` MUST return the forked token's own recorded balance if any state change has occurred for that account since the fork. | ||
| - `balanceOf(account)` MUST NOT query the source token for any checkpoint other than `checkpointedNonce`. | ||
| - `totalSupply()` MUST accurately reflect state changes in the forked token, independent of the source token. | ||
| - Any state changes MUST NOT affect the source token. | ||
|
|
||
| ## Rationale | ||
|
|
||
| ### Why Checkpoints? | ||
|
|
||
| Checkpoints enable efficient point-in-time queries. | ||
| By recording only when changes happens, the system maintains a compact, queryable history. | ||
|
|
||
| Checkpoints are also trustless and verifiable, albeit at higher gas cost for state updates. | ||
|
|
||
| ### Why Not Use Merkle Trees? | ||
|
|
||
| Merkle trees introduce complexity and off-chain dependencies. | ||
| They require users to provide proofs, which complicates UX and increases the risk of errors. | ||
|
|
||
| Merkle trees are also inefficient for continuous updates. | ||
|
|
||
| ### Lazy Balance Loading | ||
|
|
||
| For forked tokens, | ||
| querying balances from the source token only when needed (lazy loading) | ||
| significantly reduces gas costs during fork creation and initial operations. | ||
|
|
||
| ### Checkpoint Nonce Design | ||
|
|
||
| Using block numbers or timestamps as checkpoints can lead to vulnerability to reorgs and MEV attacks. | ||
| On the fork block, transactions can be re-ordered to allow balance manipulation before the fork balance is finalized. | ||
|
|
||
| Using a monotonically increasing nonce (rather than block numbers or timestamps) | ||
| ensures that the fork point is unambiguous and not susceptible to such manipulations. | ||
| This is because each state update is recorded with a unique nonce, the fork can be precisely defined. | ||
|
|
||
| ### Gas Cost Considerations | ||
|
|
||
| Forkable tokens MAY incur higher per-transaction costs due to checkpoint storage. | ||
|
|
||
| This trade-off could be acceptable for tokens where forkability provides significant value (airdrops, governance, migrations). | ||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| This EIP is fully backwards compatible with ERC-20. Forked tokens behave as standard ERC-20 tokens to any consumer. | ||
| Forkable tokens add checkpoint functionality but maintain all ERC-20 behaviors. | ||
|
|
||
| Existing contracts and wallets that interact with ERC-20 tokens will work without modification with both forkable and forked tokens. | ||
|
|
||
| ## Reference Implementation | ||
|
|
||
| TBA | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| ### Approvals | ||
|
|
||
| Approvals are not inherited. | ||
| When a token is forked, all allowances reset to zero. | ||
| Users and dApps MUST re-approve spending for the forked token. | ||
|
|
||
| ### Non-Standard ERC-20 Tokens | ||
|
|
||
| Tokens with non-standard behaviors (rebasing, fee-on-transfer, deflationary mechanisms) may not fork correctly: | ||
| - **Rebasing tokens**: Balance changes after the checkpoint may not be reflected in the fork | ||
| - **Fee-on-transfer tokens**: Forked balances will not account for fees incurred after the checkpoint | ||
|
|
||
| Auditors and integrators SHOULD carefully review source token mechanics before forking | ||
|
|
||
| ### Decimals Consistency | ||
|
|
||
| Forked tokens MUST use the same decimals as the source token to prevent balance inconsistencies and loss of precision. | ||
| Mismatched decimals MAY lead to incorrect balances and loss of precision. | ||
|
|
||
| ### Future Checkpoints | ||
|
|
||
| Forking at a future checkpoint SHOULD be prevented by implementations. | ||
| If allowed, forked token balances MAY become inconsistent and lead to accounting errors. | ||
|
|
||
| ### Checkpoint Nonce Overflow | ||
|
|
||
| Implementations should consider the implications of checkpoint nonce overflow when using a smaller uint type. | ||
|
|
||
| ## Copyright | ||
|
|
||
| This work is made available under CC0-1.0. | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.