Fungible tokens for Multi-L1 Interoperability #206
Replies: 1 comment 2 replies
-
|
Thanks for starting this discussion @cardene777 . I appreciate the high level of detail you've included, especially regarding the data flow. The protocol relies heavily on ICM messaging, with multiple messages sent between the App, Router, and Main L1s to execute a transfer, for example. At present, ICM verification costs ~500,000 gas (though future optimizations may reduce this by an order of magnitude or so). Requiring multiple ICM verifications per transfer may be prohibitively expensive for many use cases. What use cases would this standard enable that isn't already achieveable (with perhaps some tradeoffs) by ICTT? Discovery and sourcing of fragmented assets can be done off chain, requiring at most two ICM messages to link a liquidity source to a destination. Doing so on-chain, however, would require ICM messages to the Router, Main chain for the recipient, Main chain for the sender, and the two App chains involved in the transaction (correct me if my count is off here). Regardless of the approach, transfers that involve pulling in liquidity from an external chain cannot be done in a single transaction, since ICM is not atomic across chains. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Abstract
When fungible token (FT) contracts are deployed across multiple L1 blockchains, users typically need to be aware of their FT balances scattered across each L1.
Additionally, a secure mechanism is required to enable interoperability between FTs deployed on different L1s.
This standard proposes a system that allows users to interact with FTs as if they existed on a single blockchain—without needing to be aware of balances across multiple L1s—providing a seamless experience similar to interacting with a standard ERC20 token.
Motivation
There is currently no unified standard for handling ERC20-compliant FTs across multiple L1 blockchains.
To make a FT usable across multiple L1s, it must first be deployed on each of those L1s.
Additionally, when transferring a FT from one L1 to another, it is necessary to use ICM (Interchain Messaging) to bridge the tokens between L1s.
In such a setup, users need to keep track of how much FT they hold on each individual L1.
To check their total balance, users must access each L1’s FT contract, retrieve their balance, and sum them up manually.
When sending FTs across L1s, users may find that the L1 they want to send from does not hold a sufficient balance, since the total balance is scattered across multiple chains.
In this case, the user must first transfer the missing amount from a third L1 to the source L1, and only then can they proceed with sending the FT to the target L1.
As the number of participating L1s increases, the following drawbacks emerge:
These issues result in a poor user experience when FTs are fragmented across multiple L1s.
For users unfamiliar with blockchain or DApps, the complexity becomes a significant usability barrier.
This proposal outlines a system that allows users to send their FTs as if they were operating entirely within a single L1.
When a user initiates a transfer, the system automatically gathers the required amount from their scattered FT balances across different L1s.
It also determines which L1s to source the funds from automatically.
This mechanism can be executed from any L1 that has deployed a FT contract compliant with this proposal.
As a result, users can easily send FTs with a user experience similar to interacting with a single blockchain.
Moreover, the system is designed to be scalable, ensuring that performance does not degrade even as the number of supported L1s grows.
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.
This specification proposes two main components: the architecture of the L1s and the interfaces of the smart contracts.
Terminology
Main L1FT ManagerApp L1FTRouter L1RouterL1
The L1 architecture consists of three types of L1s: multiple App L1s that host individual FT contracts, multiple Main L1s that manage and execute FT transfers, and a Router L1 that handles routing to the appropriate Main L1.
Figure 1: L1 Configuration
Main L1
Main L1s (e.g., "Main L1 X" and "Main L1 Y" in Figure 1) have the following primary responsibilities:
Figure 2: FT Transfer
In Figure 1, the user holds “3 ETH” on App L1 A and “5 ETH” on App L1 B. The total balance of “8 ETH” is managed and recorded by Main L1 X.
When a user initiates a transfer from an App L1, the Main L1 receives the request and determines how much to pull from each App L1, then executes the operation accordingly.
Initially, there MAY be only one Main L1. However, as the number of L1s with deployed FT contracts increases, the number of Main L1s SHOULD also increase.
This is because all transfers are processed through Main L1s, and a growing number of users leads to higher transaction volume, which in turn increases latency.
By introducing multiple Main L1s, transaction processing can be distributed.
Each address MUST be associated with exactly one Main L1 that manages its balance.
As more Main L1s are introduced, different addresses MAY be managed by different Main L1s.
When a FT transfer is initiated, the request MUST be sent to the Main L1 managing the sender’s address.
This allows the system to distribute processing load efficiently.
As shown in Figure 2, since User A’s balance is managed by Main L1 X, the transfer request is sent only to Main L1 X. Other Main L1s do not receive the request.
App L1
An App L1 refers to an L1 where a FT contract is deployed. Examples of App L1s include:
App L1s have the following key responsibilities:
Figure 3: Balance Retrieval API
DApps on an App L1 MAY use a Balance Retrieval API to obtain the user's FT balances.
This API MUST be provided by the administrator of the corresponding Main L1, and MUST return both the total balance and per-L1 breakdown for a specified address. The API MAY be implemented using either of the following approaches:
This enables DApps on App L1s to access FT balance data from frontend or backend applications via API, enhancing user experience and integration flexibility.
Router L1
Each App L1’s FT contract maintains data about which Main L1 is managing the balance for a given address—but only if that address has previously interacted with the FT on that App L1.
For an address that has not yet interacted with the FT on a particular App L1, the contract lacks the information needed to determine whether the balance is:
Furthermore, if no Main L1 currently manages the balance, it is straightforward to assign one when there is only a single Main L1.
However, if there are multiple Main L1s, it becomes unclear which one SHOULD manage the balance for the new address.
To address this, a dedicated Router L1 is introduced. This L1 hosts a Router contract that maintains mappings between each address and the Main L1 responsible for managing its balance.
With this mechanism, FT contracts on App L1s can determine whether an address is using the FT for the first time or already has its balance managed on a specific Main L1.
Router L1 has the following responsibilities:
Figure 4: Initial Transfer Flow
Figure 4 illustrates the flow for an initial transfer using the Router L1.
The process proceeds as follows:
A DApp on App L1 B sends a FT transfer request to the FT contract.
The FT contract uses ICM to send transfer information to the Router contract on the Router L1.
The Router contract retrieves the Main L1s managing the sender and recipient addresses and forwards the transfer request to the sender’s Main L1 (Main L1 X).
The
FT Managercontract on Main L1 X performs the following actions:This system ensures that FT balance management and transfers remain scalable and well-coordinated, even as the number of participating L1s grows.
sequenceDiagram participant DApp as DApp (App L1 B) participant FT as FT Contract (App L1 B) participant Router as Router Contract (Router L1) participant ManagerX as FT Manager (Main L1 X) participant AppL1A as FT Contract (App L1 A) participant AppL1B as FT Contract (App L1 B) participant MainY as FT Manager (Main L1 Y) DApp->>FT: Send Transfer Request FT->>Router: ICM: Transfer Info Router->>ManagerX: ICM: Forward Transfer Info ManagerX->>ManagerX: Check sender has enough balance ManagerX->>ManagerX: Decide which App L1 to reduce balance alt Reduce from App L1 A ManagerX->>AppL1A: ICM: Decrease sender balance else Reduce from App L1 B ManagerX->>AppL1B: ICM: Decrease sender balance end ManagerX->>ManagerX: Decrease sender balance ManagerX->>ManagerX: Increase recipient balance alt Recipient is on Main L1 Y ManagerX->>MainY: ICM: Increase recipient balance endContract
Smart contracts are categorized into three groups based on the L1 they are deployed on: Main L1, App L1, and Router L1.
Utils
This section describes contracts that are commonly used across the system.
IAddressRegistry
An interface for a contract that manages the chain ID of the
FT Managercontract on the Main L1 responsible for tracking the balance of each address.This interface is used by the FT contracts deployed on App L1s and the Router contract deployed on the Router L1.
AccountRegistryInfoSetEmitted when information about an address and the corresponding Main L1 and FT Manager contract responsible for managing its balance is registered.
getAccountRegistryInfoA function that takes an address and returns the Main L1 and FT Manager contract responsible for managing the balance of that address.
A
mappingdefinition is required to manage the chain ID of the FT Manager contract on the Main L1 for each address.IApproveManager
IApproveManageris an interface for a contract that manages the approval information allowing FT transfer rights to be delegated to another address.It is used in the FT Manager contract on the Main L1.
SetAllowanceAn event emitted when a specific address approves another address to spend its FT.
InsufficientAllowanceAn error returned when a transfer is attempted by a spender, but the approved FT amount is insufficient.
getAllowanceA function that returns the amount of FT the
owneraddress has approved for the specifiedspenderaddress.This function MUST emit the
SetAllowanceevent.The following
mappingdefinition is required to manage approval information for each address:The function responsible for approving FT transfers MUST NOT be externally executable within the FT Manager contract on the Main L1.
This is because, by design, users are not allowed to send requests directly to the Main L1’s FT Manager contract.
All such requests MUST always be sent from the FT contract deployed on an App L1.
IBalanceManager
An interface for a contract that manages the FT balances held by addresses.
It is used in the FT Manager contract on the Main L1.
L1BalanceA struct that stores the FT balance of a given address for each App L1.
UpdateAllFTBalanceAn event emitted when the total FT balance across all App L1s managed by the Main L1 is updated.
UpdateFTBalanceAn event emitted when the FT balance of a specific App L1 managed by the Main L1 is updated.
getAllFTBalanceA function that returns the total FT balance held by a specific address across all App L1s.
getFTBalanceA function that returns the FT balance held by a specific address on a specific App L1.
getFTBalancesA function that returns an array of FT balances across all App L1s for a specific address.
The returned array MUST be sorted in descending order by balance.
A
mappingdefinition like the following is required to manage balance information for each address:IBlocklist
An interface for a contract that manages addresses prohibited from using the FT.
It is used in the FT Manager contract on the Main L1, the FT contract on the App L1, and the Router contract on the Router L1.
UserLocalBlockedAn event emitted when a specific address is added to the blocklist on the App L1 only.
UserGlobalBlockedAn event emitted when a specific address is added to the blocklist across the entire system (all Main L1s, App L1s, and the Router L1).
UserLocalUnblockedAn event emitted when a specific address previously blocklisted only on the App L1 is removed from the blocklist.
UserGlobalUnblockedAn event emitted when a specific address previously blocklisted across the entire system (all Main L1s, App L1s, and the Router L1) is removed from the blocklist.
LocalBlockedUserAn error returned when an address blocklisted on the App L1 attempts to transfer FTs.
GlobalBlockedUserAn error returned when an address blocklisted across the entire system (all Main L1s, App L1s, and the Router L1) attempts to transfer FTs.
isLocalBlockedA function that checks whether a given address is blocklisted only on the App L1.
isGlobalBlockedA function that checks whether a given address is blocklisted across the entire system (all Main L1s, App L1s, and the Router L1).
The following mappings are required to manage blocklist status for each address:
_localBlockedStores information for addresses blocklisted specifically on the FT contract deployed on an App L1.
_globalBlockedStores information for addresses blocklisted across the entire system, including the FT Manager on the Main L1, the FT contract on the App L1, and the Router on the Router L1.
ILock
An interface for a contract that manages FTs locked on the App L1’s FT contract.
It is used within the FT contract deployed on the App L1.
This lock mechanism can be restricted for use only on the App L1.
Locked balances are not utilized by the FT processing logic within the FT Manager contract on the Main L1.
Requests are sent from the FT contract on the App L1 to the FT Manager contract on the Main L1.
LockAn event emitted when the FT held by a specific address is locked.
UnlockAn event emitted when the lock on the FT held by a specific address is released.
setLockA function that locks the FT held by a specific address.
lockedAmountA function that retrieves the amount of FT locked for a specific address.
A
mappingdefinition is required to manage the locked FT balance held by each address.ILockManager
An interface for a contract that manages the locked FT balances on the FT contract deployed on the App L1.
It is used in the FT Manager contract.
LockAn event emitted when the FT held by a specific address is locked.
UnlockAn event emitted when the lock on the FT held by a specific address is released.
lockedAmountA function that retrieves the amount of FT locked for a specific address.
A
mappingdefinition is required to manage the locked FT balances for specific addresses on specific App L1s.IL1RegistryBase
An interface for a contract that manages references to contracts deployed on other L1s, used by the FT Manager contract on the Main L1, the FT contract on the App L1, and the Router contract on the Router L1.
It is used across all three: the FT Manager on the Main L1, the FT contract on the App L1, and the Router contract on the Router L1.
Since all L1s MUST maintain the same set of information, any registration or update of a chain ID and corresponding contract address MUST always be initiated from the Router contract on the Router L1.
The Router contract then sends the update requests via ICM to the FT Manager contract on the Main L1 and the FT contracts on each App L1.
For this reason, the functions responsible for registering or updating the chain ID and contract address MUST be defined as internal functions, and MUST only be called when triggered through ICM requests.
L1InfoSetAn event emitted when a new L1 chain ID and its corresponding contract address are registered.
A
mappingdefinition is required to manage the association between L1 chain IDs and contract addresses.IRouterRegistry
An interface for a contract that manages the chain ID of the Router L1 and the address of the Router contract deployed on it.
It is used in the FT Manager contract on the Main L1 and the FT contract on the App L1.
Since all Main L1s and App L1s MUST maintain the same information, any registration or update of the Router L1's chain ID and Router contract address MUST always be executed from the Router contract on the Router L1.
The Router contract then sends requests via ICM to update the corresponding data in the FT Manager contract on the Main L1 and the FT contract on the App L1.
Therefore, the functions responsible for setting or updating the chain ID and contract address MUST be defined as internal functions and MUST only be invoked in response to ICM requests.
RouterInfoSetAn event emitted when the Router L1’s chain ID and the Router contract address are newly registered.
getRouterInfoA function that retrieves the currently registered Router L1 chain ID and Router contract address.
IFTManagerRegistry
An interface for a contract that manages information about the Main L1s responsible for FT balance management and their corresponding FT Manager contract addresses.
It is used in the FT Manager contract on the Main L1, the FT contract on the App L1, and the Router contract on the Router L1.
This interface MUST inherit from the following:
IL1RegistryBaseSince all Main L1s and App L1s MUST maintain the same set of information, any registration or update of a Main L1’s chain ID and FT Manager contract address MUST always be executed from the Router contract on the Router L1.
The Router contract then sends requests via ICM to the FT Manager contract on the Main L1 and the FT contract on the App L1 to apply the update.
Therefore, functions for setting or updating the chain ID and contract address MUST be defined as internal and invoked only when triggered via ICM.
FTManagerInfoSetAn event emitted when information about a Main L1 and its corresponding FT Manager contract is added.
getFTManagerAddressA function that retrieves the registered information for a specific Main L1 and its FT Manager contract.
getFTManagerInfosA function that retrieves the registered information for all Main L1s and their associated FT Manager contracts.
IFTRegistry
An interface for a contract that manages the chain IDs of App L1s and the addresses of FT contracts deployed on them.
It is used in the FT Manager contract on the Main L1 and the Router contract on the Router L1.
This interface MUST inherit from the following:
IL1RegistryBaseSince all Main L1s and the Router L1 MUST maintain the same set of information, any registration or update of an App L1’s chain ID and FT contract address MUST always be executed from the Router contract on the Router L1.
The Router contract then sends update requests via ICM to the FT Manager contract on the Main L1 and to the FT contracts on the App L1s.
Therefore, functions for setting or updating chain IDs and contract addresses MUST be defined as internal functions and invoked only when triggered via ICM.
FTInfoSetAn event emitted when information about a FT contract on an App L1 is added.
getFTAddressA function that retrieves the registered information of the FT contract on a specific App L1.
getFTInfosA function that retrieves the registered information of all FT contracts across all App L1s.
ITeleporterManager
An interface for a contract that manages information about the Teleporter contract and other necessary data used when communicating with other L1s via ICM.
It is used in the FT contract on the App L1, the FT Manager contract on the Main L1, and the Router contract on the Router L1.
ExecutionPolicyA struct that defines default values required when sending a request to another L1 via ICM.
A
mappingmust be defined to manage default values per destination L1:The structure includes the following fields:
feeTokenAddressThe contract address of the token (either native or ERC20) used for fees.
amountThe amount of tokens used as the fee.
requiredGasLimitThe gas limit required for execution.
allowedRelayerAddressesA whitelist of relayer addresses authorized to forward requests.
TeleporterSetAn event emitted when the chain ID and Teleporter contract address for an L1 are added or updated.
ExecutionPolicySetAn event emitted when a new or updated
ExecutionPolicyis set.InvalidTeleporterAn error returned when data is received from an unknown chain ID or from an unauthorized Teleporter contract.
receiveTeleporterMessageA function for receiving data sent from another L1.
setTeleporterA function for adding or updating the chain ID and Teleporter contract information for an L1.
setExecutionPolicyA function for adding or updating an
ExecutionPolicy.getTeleporterMessengerA function that retrieves the Teleporter contract information associated with the contract implementing this interface.
getChainIdA function that retrieves the chain ID of the L1 on which the contract implementing this interface is deployed.
getTeleporterA function that retrieves the Teleporter contract address associated with a specific L1 chain ID.
getExecutionPolicyA function that retrieves the
ExecutionPolicyassociated with a specific L1 chain ID.getCurrentL1InfoA function that retrieves the chain ID and Teleporter contract information for the L1 on which the contract implementing this interface is deployed.
Each contract implementing this interface MUST define the chain ID of the L1 it is deployed on, as well as the corresponding Teleporter contract address.
Additionally, the interface MUST manage the information of Teleporter contracts on all connected L1s.
Struct
This section summarizes the
enumandstructtypes used across the contracts.They are used in the FT contract on the App L1, the FT Manager contract on the Main L1, and the Router contract on the Router L1.
ActionAn
enumused to identify the type of operation requested when receiving a message from an App L1 FT contract on the Router L1 or Main L1.The system executes a process based on the specified action type.
mintIssues new FTs.
Triggered by the FT Manager on the Main L1 and sent to the FT contract on the App L1.
transferTransfers FTs.
Triggered by the FT contract on the App L1 and sent to the FT Manager on the Main L1.
transferFromTransfers FTs from an address other than the token holder.
Triggered by the App L1 FT contract and sent to the Main L1.
localTransferTransfers FTs between addresses on the same App L1.
Triggered by the App L1 FT contract and sent to the Main L1.
approveGrants permission to another address to operate the FTs.
Triggered by the App L1 FT contract and sent to the Main L1.
burnBurns FTs.
Triggered either from the Main L1 to the App L1 or vice versa.
burnFromBurns FTs from an address other than the token holder.
Triggered either from the Main L1 to the App L1 or vice versa.
updateTokenBalanceUpdates balance information.
Triggered either from the Main L1 to the App L1 or from the App L1 to the Main L1.
updateTokenAllowanceUpdates the allowance for a spender address.
Triggered by the App L1 FT contract and sent to the Main L1.
blockAdds an address to the blocklist.
Triggered by the Router on the Router L1 and sent to the Main L1 and App L1.
unblockRemoves an address from the blocklist.
Triggered by the Router on the Router L1 and sent to the Main L1 and App L1.
lockLocks FTs held by an address on the App L1.
Triggered by the App L1 FT contract and sent to the Main L1.
multiCallExecutes multiple actions in a batch.
setLatestMainL1InfoUpdates the latest Main L1 and FT Manager contract info.
Triggered by the Router L1 and sent to the Main L1 and App L1.
setTeleporterAdds a new L1 and its Teleporter contract info.
Triggered by the Router L1 and sent to the Main L1 and App L1.
setRouterInfoSets or updates the Router L1 and Router contract info.
Triggered by the Router L1 and sent to the Main L1 and App L1.
setFTManagerInfoSets or updates the Main L1 and FT Manager contract info.
Triggered by the Router L1 and sent to the Main L1 and App L1.
setFTInfoSets or updates the App L1 and FT contract info.
Triggered by the Router L1 and sent to the Main L1.
L1InfoA struct that holds L1-related information.
Includes the L1 chain ID (
bytes32) and the associated contract address.SendMessageParamsA struct that contains parameters required to send a request to another L1 via ICM.
Fields include:
destinationBlockchainIDThe chain ID of the destination L1.
destinationAddressThe contract address on the destination L1.
feeInfoInformation about the fee token and amount.
requiredGasLimitThe gas limit required to execute the process.
allowedRelayerAddressesA list of relayer addresses permitted to forward the request.
messageThe content of the request to be executed.
Main L1
The following interfaces and contracts MUST be implemented and deployed on the Main L1:
IApproveManagerManages information about addresses whose balances are tracked by this FT Manager contract and who have granted spending approval to other addresses.
IBalanceManagerManages the balances of specific addresses within this FT Manager contract.
IBlocklistManages the blocklist of addresses whose balances are tracked by this FT Manager contract.
ILockManagerManages the balances of FTs locked in FT contracts deployed on specific App L1s.
IRouterRegistryManages the chain ID and contract address information of the Router L1.
IFTRegistryManages FT contracts deployed on App L1s and restricts usage to only those that are registered.
IFTManagerRegistryManages other FT Manager contracts deployed on different Main L1s and restricts usage to only those that are registered.
ITeleporterManagerImplements functionality for sending and receiving requests to and from contracts on other L1s via ICM.
To receive requests via ICM, the contract MUST also conform to the
ITeleporterReceiverinterface.receiveTeleporterMessageA function called when a request is received from a FT contract on an App L1 or from a FT Manager contract on another Main L1.
This function executes the requested FT operation.
The contract MUST conform to the
IFTManagerinterface.transferA function that transfers FTs held by a specific address.
The following validations MUST be performed:
- Ensure that neither the sender nor the recipient address is blocklisted.
- Ensure that the sender’s balance is sufficient to cover the transfer amount.
transferFromA function that transfers FTs from an address other than the token holder.
The following validations MUST be performed:
- Ensure that the caller, sender, and recipient addresses are not blocklisted.
- Ensure that the sender’s balance is sufficient to cover the transfer amount.
- Ensure that the caller has sufficient allowance from the sender to cover the transfer amount.
approveA function that grants another address permission to spend FTs.
The following validations MUST be performed:
- Ensure that neither the caller nor the spender address is blocklisted.
burnA function that burns (i.e., sends to the zero address) FTs held by a specific address.
The following validation MUST be performed:
- Ensure that the caller is not blocklisted.
burnFromA function that burns (i.e., sends to the zero address) FTs from an address other than the token holder.
The following validations MUST be performed:
- Ensure that both the caller and the token holder are not blocklisted.
App L1
The following interfaces and contracts MUST be implemented and deployed on the App L1:
AddressManagerManages the information of the FT Manager contract on the Main L1 and tracks which Main L1 manages the balance of each address.
IBlocklistManages the blocklist of addresses for this FT contract.
It contains both addresses blocked only on this App L1 and addresses blocked system-wide (across all Main L1s, App L1s, and the Router L1).
ILockManages the FTs locked by specific addresses.
IRouterRegistryManages the chain ID and contract information of the Router L1.
IFTRegistryManages other FT contracts deployed on App L1s and restricts usage to only those that are registered.
IFTManagerRegistryManages the FT Manager contracts on Main L1s and restricts usage to only those that are registered.
ITeleporterManagerImplements the functionality for sending and receiving requests to and from other L1s via ICM.
In addition, the contract MUST inherit from the ERC-20 standard.
transferTransfers FTs held by a specific address.
The following validation MUST be performed:
- Ensure that neither the sender nor the recipient address is blocklisted.
The default values defined in the
ExecutionPolicyfor each chain ID are used.transferFromTransfers FTs from an address other than the token holder.
The following validation MUST be performed:
- Ensure that the caller, sender, and recipient addresses are not blocklisted.
If the FT balance on the App L1 is sufficient, the transfer is processed locally and only the result is sent to the Main L1.
The default values defined in the
ExecutionPolicyfor each chain ID are used.approveGrants another address permission to spend FTs.
The following validation MUST be performed:
- Ensure that neither the caller nor the spender address is blocklisted.
The default values defined in the
ExecutionPolicyfor each chain ID are used.The contract MUST also conform to ERC20BurnableUpgradeable.
burnBurns FTs (sends to the zero address) held by a specific address.
The following validation MUST be performed:
- Ensure that the caller is not blocklisted.
The default values defined in the
ExecutionPolicyfor each chain ID are used.burnFromBurns FTs from an address other than the token holder.
The following validation MUST be performed:
- Ensure that both the caller and the token holder are not blocklisted.
The default values defined in the
ExecutionPolicyfor each chain ID are used.SelfTransferAn error returned when the sender and recipient of a FT transfer are the same address.
To avoid unnecessary requests that could impact L1 processing performance, transfers where the sender and recipient are the same MUST be rejected.
localTransferA function that sends FTs held on the source App L1 to a FT contract deployed on another App L1 via ICM.
localBlockUserA function that adds a specific address to the blocklist only on the L1 where this contract is deployed.
This function MUST be restricted to authorized addresses only.
localUnblockUserA function that removes a specific address from the blocklist on the L1 where this contract is deployed.
This function MUST be restricted to authorized addresses only.
In cases where the FT contract on the App L1 already knows which Main L1 manages the address balance, requests MUST be sent directly to the FT Manager contract on that Main L1 without routing through the Router contract on the Router L1.
This helps reduce unnecessary requests.
The contract MUST conform to the
ITeleporterReceiverinterface to be able to receive requests via ICM.receiveTeleporterMessageA function invoked when a request is received from a FT Manager contract on another Main L1 or from the Router contract on the Router L1.
This function executes the requested FT operation.
If any post-processing is required after sending the request, it is RECOMMENDED to include that logic within this function for improved security.
Router L1
The following interfaces and contracts MUST be implemented and deployed on the Router L1:
IAddressRegistryManages information about FT Manager contracts on Main L1s and tracks which Main L1 is responsible for managing the balance of each address.
IBlocklistManages the blocklist of addresses tracked by this Router contract.
Addresses managed by the Router contract MUST be blocklisted across the entire system (all Main L1s, App L1s, and the Router L1).
IRouterRegistryManages the chain ID and contract address of the Router L1.
IFTRegistryManages FT contracts on App L1s and restricts usage to only those that are registered.
IFTManagerRegistryManages FT Manager contracts on Main L1s and restricts usage to only those that are registered.
ITeleporterManagerImplements functionality for sending and receiving requests to and from other L1s via ICM.
blockUserA function that adds a specific address to the blocklist.
Sends requests to the FT Manager contracts on the Main L1 and the FT contracts on the App L1.
unblockUserA function that removes a specific address from the blocklist.
Sends requests to the FT Manager contracts on the Main L1 and the FT contracts on the App L1.
setLatestMainL1InfoA function that adds or updates the latest Main L1 chain ID and FT Manager contract information.
Sends requests to the FT Manager contracts on the Main L1 and the FT contracts on the App L1.
setFTInfoA function that adds or updates the chain ID and contract information of an App L1 FT contract.
Sends a request to the FT Manager contract on the Main L1.
setFTManagerInfoA function that adds or updates the chain ID and contract information of a Main L1 FT Manager.
Sends requests to the FT Manager contracts on the Main L1 and the FT contracts on the App L1.
setRouterInfoA function that adds or updates the chain ID and contract address of the Router L1.
Sends requests to the FT Manager contracts on the Main L1 and the FT contracts on the App L1.
Flow
Requests from App L1
The flow that occurs when a request involving FT usage is initiated from a DApp on an App L1.
sequenceDiagram autonumber participant DApp as DApp (App L1) participant SC1 as FT (App L1) participant SC2 as FT (Other App L1) participant Router as Router participant SCM1 as FT Manager (Main L1) participant SCM2 as FT Manager (Other Main L1) %% Initial request DApp ->> SC1: FT transfer request Note over DApp, SCM2: If the Main L1s managing the balances of the sender and recipient addresses are already known,<br>the FT contract retrieves the FT Manager’s chain info and forwards the request. alt The Main L1s managing the balances of the sender and recipient<br>are already registered in the App L1 FT contract (*①) SC1 ->> SCM1: forward transfer request SCM1 ->> SCM1: validate alt validation fails SCM1 ->> SCM1: revert with error end SCM1 ->> SCM1: execute transfer logic alt Request must be sent back to the originating App L1 FT SCM1 ->> SC1: send follow-up request end alt Request must be sent to another App L1 FT SCM1 ->> SC2: send follow-up request end SCM1 ->> SCM1: update sender balance alt recipient’s balance is managed by a different Main L1 SCM1 ->> SCM2: send balance update request else SCM1 ->> SCM1: update recipient balance end else The Main L1s managing the sender and recipient balances<br>are not registered in the App L1 FT contract SC1 ->> Router: forward transfer request Router ->> Router: determine Main L1s managing sender and recipient balances alt sender’s balance is managed by SCM1 Router ->> SCM1: forward transfer request else Router ->> SCM2: forward transfer request end Note over DApp, SCM2: Follow-up processing is the same as in step ① endBlocklist
A mechanism is REQUIRED to prevent specific addresses from using the FT in cases where they attempt to acquire it fraudulently or engage in malicious behavior (e.g., improperly acquiring other ERC20 tokens and converting them into FT).
This functionality MUST be implemented in the FT Manager contract on the Main L1, the FT contract on the App L1, and the Router contract on the Router L1.
The reason this function MUST also be available on the App L1 FT contract is that, in cases where the sending amount is fully covered by the balance on the App L1, the FT contract sends only the result to the FT Manager contract on the Main L1.
Figure 5: Blocklist management on Main L1 and App L1
There are two possible routes for registering an address to the blocklist, as illustrated in Figure 5:
The first route is initiated by an authorized address on the Router L1, which calls the function on the Router contract to register or remove an address from the blocklist.
During this process, the Router contract MUST send requests to both the FT Manager contracts on the Main L1 and the FT contracts on the App L1 to add the specified address to their respective blocklists.
This ensures that the address is prevented from using FT system-wide.
Additionally, the system MUST manage data in a way that clearly indicates the address is globally blocklisted.
The second route allows an administrator of the App L1 FT contract to register or remove an address from the blocklist locally.
This restricts the address from using FT only on that specific App L1.
The first method enables a global block across the system but requires evaluation and execution by the administrator of the Router contract.
As such, even if an administrator on an App L1 submits a request to add an address to the blocklist, the action MAY not be processed immediately.
Therefore, the second method provides a way to quickly block FT usage at the App L1 level, helping to mitigate potential damage swiftly.
Lock
Typically, a request to the FT Manager contract on the Main L1 requires a minimum processing time of 6 seconds.
Example: App L1 (2s) → Main L1 (2s) → App L1 (2s)
However, there are cases where faster processing is REQUIRED on specific App L1 chains when using FT, such as:
To support such use cases, the system provides a mechanism called Lock, which enables faster FT usage by locking a portion of the user's balance, making it usable only on a specific App L1.
Lock Flow
Figure 6: Locking FT
In Figure 6, User A locks 5 ETH worth of FT on an App L1 FT contract.
Precondition: User A owns 4 ETH on App L1 A, 5 ETH on App L1 B, and 3 ETH on App L1 C.
If the managing Main L1 is unknown, the request is instead sent to the Router contract on the Router L1.
This ensures that locked FT is NOT used in requests relayed through Main L1.
Using Locked FT
Figure 7: Transferring Locked FT
To use locked FT, the system provides a dedicated function called
lockTransfer.When this function is called, the FT contract on the current App L1 first processes the transfer using the locked balance.
It then sends a request to the FT Manager contract on the Main L1 to reflect the deduction.
The transfer flow in Figure 7 is as follows:
As a result, the minimum processing time becomes 4 seconds.
Example: App L1 (2s) → Main L1 (2s)
Furthermore, since the App L1 contract executes the transfer first and only sends the result to the Main L1, follow-up Dapp operations MAY proceed immediately, making the effective latency as low as 2 seconds.
Integration with the
transferFunctionIn addition to using the
lockTransferfunction, the functionality MAY also be integrated into thetransferfunction. This allows the following behavior:lockTransferlogic is invoked internally.To ensure compatibility with ERC-20 Dapps, it is RECOMMENDED to integrate this behavior into the
transferfunction.However, in cases where users require full control over when locked funds are used, it MAY be PREFERRED to invoke the
lockTransferfunction explicitly rather than integrating it intotransfer.Balance Transfers Between App L1 Chains
When FT is locked, the amount that can be locked is limited to the balance held on the current App L1 chain.
Therefore, to increase the amount of assets that can be locked, it is NECESSARY to enable balance transfers between App L1 chains.
To achieve this, a function called
localTransferMUST be implemented.When the
localTransferfunction is executed, a request is sent to the FT Manager contract on the Main L1 that manages the caller’s balance.This contract determines how much FT should be transferred from each App L1 chain to the requesting App L1’s FT contract.
At this time, both the locked FT and the FT balance already held on the requesting App L1 MUST be excluded from the transfer calculation.
After this determination, requests are sent to the FT contracts on each relevant App L1.
This mechanism enables seamless FT balance transfers across App L1 chains.
Restrictions on Teleporter Contracts
On each L1 chain, the deployed Teleporter contract MUST be configured to only accept requests from the following contracts deployed on the same L1:
This restriction ensures that even if another contract on the same L1 attempts to send a request via ICM to a different L1, the request will FAIL unless it originates from an authorized contract.
By enforcing this restriction, unauthorized requests via ICM can be effectively prevented.
Execution Policy
When sending a request using ICM from contracts within the system (including all Main L1s, App L1s, and the Router L1), it is often difficult to determine the precise execution path of the request at the time of submission. As a result, specifying the following parameters becomes challenging:
feeTokenAddress: The token contract used for relayer fees.amount: The amount of tokens used for relayer fees.requiredGasLimit: The gas limit for transaction execution.allowedRelayerAddresses: An array of permitted relayer addresses.Therefore, it is RECOMMENDED that each L1 contract implements a mechanism to manage default execution information (Execution Policy) for each destination L1, as shown below:
This allows contracts to independently restrict relayers and fee tokens per destination L1, enabling custom execution control per L1.
However, note that if the fee is insufficient, the transaction will fail. Thus, it is RECOMMENDED not to set default values for fee-related fields, allowing more flexibility for fee adjustments.
Only in the case of requests sent from FT contracts on App L1s, these parameters MAY be specified directly during the request. In such cases, the
requiredGasLimitfield SHALL always use the default value.This is necessary because gas costs differ by App L1. For example, an App L1 with zero gas fees could overload the system by submitting excessive transactions, potentially causing congestion on the Main L1. Setting a default
requiredGasLimitvalue ensures consistent limits across App L1s and helps control resource usage.FT contracts'
externalandpublicfunctions MAY accept execution policy parameters as input arguments. However, additional validations SHOULD be performed—for example, disallowing relayer addresses not included in the default policy, or enforcing array length restrictions.An alternative implementation MAY ignore any values provided outside the default configuration and enforce the default execution policy rigidly.
Managing
totalSupplyThere are two main approaches to tracking the total supply of the FT:
1. On-chain tracking
2. Off-chain tracking (RECOMMENDED)
While the off-chain approach is preferred for efficiency, implementing the on-chain method as well can be useful when contracts themselves need to reference the total supply.
Rationale
Why FT Balances Are Managed on Main L1
FT balances held on App L1s are managed on a Main L1 to enable a unified source of truth for balance queries. Although it is possible to manage balances across App L1s off-chain, this would make it difficult to retrieve the full balance on-chain. By consolidating balance management on the Main L1, it becomes possible to perform balance validation at the time of
transferoperations.Why Multiple Main L1s Are Needed
Having a single Main L1 would simplify processing and eliminate the need for a Router L1. However, this can lead to congestion on the Main L1 as the volume of requests increases, resulting in slower transaction processing. To address this, the system SHOULD utilize multiple Main L1s to distribute processing load and prevent bottlenecks. This proposal does not define how many addresses each FT Manager on a Main L1 SHOULD manage—that is left for future decisions.
Is It Necessary to Have Multiple Main L1s?
In the initial stages, a single Main L1 MAY be sufficient. However, as the number of App L1s and users increases, transaction volume will also increase, potentially degrading user experience (UX) due to longer confirmation times. To maintain performance, deploying multiple Main L1s is RECOMMENDED. The criteria for how many addresses a single Main L1 SHOULD manage is not defined in this proposal and will need to be determined later.
Why Transfers Are Not Executed Directly from App L1
In this proposal, the FT Manager contract on the Main L1 determines how much FT to transfer from each App L1 and executes the transfer. While it is technically possible to initiate the transfer directly from an App L1, doing so would require fetching the user's total balance from the Main L1, resulting in additional cross-chain transactions. Furthermore, not all App L1s are interconnected via ICM. Since the Main L1 acts as the central hub for all App L1s, all cross-App L1 FT transfers SHOULD be executed from the Main L1.
When FT Balance on App L1 Is Sufficient
If a transfer request is made and the transfer amount is fully covered by the sender’s FT balance on the App L1, and both the sender’s and recipient’s Main L1 and corresponding FT Manager contract are known, the App L1 MAY execute the transfer locally and send only the result to the FT Manager on the Main L1. This helps AVOID unnecessary requests to the FT Manager contract.
When a FT Contract Is Arbitrarily Deployed
It is possible for unauthorized FT contracts to be deployed on various App L1s. However, actual FT transfer logic is executed on the FT Manager contract on the Main L1, which MAINTAINS a registry of authorized FT contracts deployed on App L1s. Any request from a contract not listed in this registry WILL FAIL.
Router L1 May Be Cost-Prohibitive
Operating an L1 incurs significant annual costs. Minimizing the number of L1s DEPLOYED is RECOMMENDED for both cost and manageability. Since Router L1 is responsible for maintaining a mapping of which Main L1 manages a given address, it MAY be consolidated into the first Main L1. In that case, FT contracts on each App L1 CAN forward requests to the first Main L1 if they cannot determine which Main L1 manages a particular address. Additional Main L1s beyond the first DO NOT NEED to deploy a Router contract and MUST only reference the Router's deployed location.
Handling Consecutive Transactions
It is possible that multiple transfer requests are sent simultaneously from FT contracts on different App L1s. Because transfer and burn operations are ALWAYS processed by the FT Manager on the Main L1 that manages the sender’s balance, requests WILL BE handled in sequence. This ENSURES that no more FT is consumed than is available in the sender’s balance.
Transfer Optimization Logic
When executing a transfer, the FT Manager contract on the Main L1 MUST determine from which App L1s and in what proportions the sender’s FT should be sourced. Although this is beyond the scope of this proposal, the following allocation strategy is RECOMMENDED: it attempts to use the minimum number of App L1s and avoids leaving small residual balances ("dust"). The process is composed of three stages:
Single-Chain Exact Match
Two-Chain Exact Match (Two-Pointer Technique)
Sort balances in descending order and use two pointers to find a pair that sums to the transfer amount.
Example:
[8, 4, 3, 2, 1, 1], Target:6left = 0(8),right = 5(1)8+1=9→ too much →left = 14+1=5→ too little →right = 44+1=5→ still too little →right = 34+2=6→ exact match → use balances from those two chainsSuffix Accumulation Fallback
KApp L1s until the transfer amount is met.This method maintains an O(N log N) complexity due to sorting, and it helps reduce gas usage and leftover balances efficiently.
Monitoring and Automatic Retry
In this proposal, it is RECOMMENDED to monitor events emitted by the Teleporter contract (TeleporterMessenger) and perform automatic retries or alerts in case messages sent via ICM get stuck and are not processed on the destination L1.
Events to Monitor
revert) after receiving the request.Monitoring Flow
Monitoring
Monitor all events emitted by
TeleporterMessengerfrom contracts within the system (FT Manager on Main L1, FT on App L1, Router on Router L1).Status Management
Track the status and information of each message by its
messageId.Failure Detection
If
ReceiveCrossChainMessageis emitted butMessageExecutedis not, or ifMessageExecutionFailedis emitted, classify it as "incomplete".Automatic Retry
Retry execution of the request.
Alerts / Final Judgment
If retries fail N times, notify the operations team.
(The number of allowed retries is outside the scope of this proposal.)
Take manual action if necessary.
Contract Upgrade
All contracts SHOULD be upgradeable. Making contracts upgradeable allows the logic to be modified without changing the contract address used for calls. This also makes it easier to patch vulnerabilities.
Upgrade functions MUST be restricted to authorized addresses, which are RECOMMENDED to be controlled by the Main L1 operator.
Initial Mint on Contract Deployment
Figure 8: Initial Mint of FT
When deploying the FT contract on App L1, initial minting MAY be required.
When deploying the FT contract on App L1, it is deployed by the administrator of Router L1, and the initial FT minting is performed in the initialization function (
constructororinitialize).At this time, the FT contract does not store information about which Main L1 and FT Manager contract manages each address’s balance.
Therefore, the initialization function MUST accept the mapping of already managed addresses to their Main L1 and FT Manager contract as arguments.
For addresses with known Main L1 and FT Manager, requests MUST be sent directly to the corresponding Main L1.
For addresses not yet associated with a manager, requests MUST be sent to Router L1.
This enables efficient initial minting.
When to Scale Main L1
There is a limit to the number of transactions that can be processed on a single Main L1. If the volume of transactions becomes too high, processing times will increase. Therefore, this proposal takes an approach that distributes processing load by increasing the number of Main L1s responsible for managing balances per address. In such cases, it is necessary to establish indicators for when to scale Main L1. While outside the scope of this proposal, relevant indicators are summarized below:
The first indicator is TPS (transactions per second). If the number of incoming requests exceeds the TPS that can be handled by the Main L1, additional Main L1s SHOULD be introduced. Rather than waiting until the TPS threshold is reached, it is RECOMMENDED to add a new Main L1 when about 80% of the TPS capacity is utilized to allow for a buffer.
The second indicator is the number of addresses. As the number of addresses with managed balances increases, the likelihood of a higher transaction volume also increases. Even if TPS is not currently a problem, a sudden spike in transaction count may occur at some point, making this a valuable metric when considering whether to add a new Main L1.
The third indicator is transaction execution time. If transactions take a long time to execute, this may indicate an accumulation of pending transactions, suggesting the need for more Main L1s. While execution time is generally influenced by TPS, it is still a useful reference metric.
These indicators can only be validated through actual measurement during implementation. In addition, if other metrics prove useful, they SHOULD also be tracked.
Backwards Compatibility
This proposal maintains backwards compatibility between the
FTcontract and ERC-20.Reference Implementation
Security Considerations
Main L1 and Router L1 Providers
The providers of the Main L1 and the Router L1 MUST be the same. If they differ, the Main L1 would have to trust the Router L1, and any malicious logic within the Router L1 could be executed as is. It is also possible to integrate the Router L1 into the Main L1. For more details, refer to the section "High Costs of Deploying a Router L1".
Unauthorized Requests from Arbitrary L1s or Contracts
L1s or contracts using the FT MAY attempt to send requests—such as FT transfers—to system contracts (i.e., the FT Manager contract on the Main L1, the FT contract on App L1s, and the Router contract on the Router L1). However, in this proposal, each contract maintains a whitelist of allowed requests received via ICM. Any request from an unapproved L1 or contract will fail execution.
Operation as a Unified System
This system is composed of the following three categories of L1s and contracts:
Each L1 deploys a Teleporter contract that restricts which contracts can receive requests. Each contract validates the origin L1 and the sender Teleporter contract when receiving a request via ICM. As a result, only authorized L1s and Teleporter contracts, as well as the designated system contracts (FT Manager, FT, and Router), are permitted to send requests, preventing unauthorized L1s or contracts from injecting invalid requests.
Double Updating of totalSupply
When updating
totalSupply, events MUST be monitored to update the database that supports the off-chain API. These events SHOULD be emitted from the App L1’s FT contract. If the system instead monitors events from the FT Manager contract on the Main L1, there is a risk: if a request from the FT Manager to the App L1’s FT contract fails, the FT Manager would have already updatedtotalSupply, creating a mismatch between on-chain and off-chain states.Copyright
Copyright and related rights waived via CC0.
Beta Was this translation helpful? Give feedback.
All reactions