Proposal for a standard L2->L1 bridge, for tokens minted on StarkNet

The proposal is not (yet) formulated as an official improvement proposal and is currently presented to gauge the interest and get feedback.


The usual flow for minting a new ERC-20 or ERC-721 token on an Ethereum L2 is:

  1. Mint the token on L1
  2. Mint the token on L2
  3. Link them together with the appropriate bridge.

So even if there is no immediate need to deploy the token on L1, minting has to be initially done on L1, and only then bridged to L2. This creates unnecessary cost overhead and requires the deployer to be knowledgeable of development in both environments, and their bridging functionality.

Furthermore, if token governance is handled on L2, it makes little sense to create a complex L1 token that mimics the functionality of the L2 token.
It should be possible to mint the token on L2, and then easily port it to L1, keeping only the functionality required to enable interoperability and settlement. ( e.g discussion thread)

This proposal describes a standard that enables developers to first mint tokens on L2 and then use a known and secure bridge to port the token to L1.

The standard heavily uses the ability of StarkNet’s L1↔L2 messaging. The mechanism allows messages to be passed relatively quickly between L2 and L1, by any contract and with any data. This allows for both fungible and non-fungible assets to be transferred without relying on third party liquidity providers or waiting for long timeouts.


The standard requires the following components:

  • A bridge per standard (e.g. ERC-20, ERC-721) on L1, receives a request to create an L1 instance of a token from L2.
  • The asset minted on L2 must support the L2Bridgable interface, and be aware of the bridge address on L1.

L1 Bridge

L1 Bridge operation

The bridge on L1 serves several purposes.

  1. Creates a new token instance on L1 after receiving a request from L2
  2. Keeps a mapping for L1↔L2 addresses (can also be calculated without storage costs)
  3. Handles requests to bridge tokens from L2
  4. Handles requests to bridge tokens to L2

Extra benefits:

  • Since only basic token functionality is required, the bridge creates a standard token implementation instance, with all the best practices in mind. It does not require the token deployer on L2 to be mindful of the implementation on L1.
  • Since all L1 functionality is standard, all new instances only maintain storage, and delegate the calls to the factory contract, making the mining of the original contract on L1 substantially cheaper.

Bridge Interface

  • createL1Instance:
    • Clones the contract, with the l2_address as the salt for CREATE2
    • Receives:
      • L2 Token address (sent implicitly from L2)
      • Token name
      • Token symbol
    • These must match the message sent by the L2 token
  • BridgeTokensFromL2:
    • Mints new tokens on L1
    • Requires the contract on L1 to be initiated
    • Receives:
      • L2 Token address (sent implicitly from L2)
      • L1 recipient
      • Token Amount / ID
  • BridgeTokensToL2
    • Burns the tokens of the sender on L1 and sends a message to L2
    • Receives:
      • L2 token address
      • L2 recipient
      • Token amount / ID

L1Bridgable interface (ERC-20 example)

The interface has the following functions

func create_l1_instance( name : felt, symbol : felt )

Sends the message to the known L1 bridge. The message passes the contract address, the token name, the token symbol.
(In ERC721 it should also send the URI of the token and possibly other metadata)

func bridge_tokens_to_l1( l1_recipient : felt, amount : Uint256)

Once an instance on L1 is created, tokens can be bridged directly from the contract to L1.

  • Sends a message to a known bridge on L1
  • Burns/locks the tokens of the message sender on L2
  • Message to L1 includes:
    • L1_recipient
    • Token amount / ID

func bridge_tokens_from_l1( from_address : felt, l2_recipient : felt, amount : Uint256)

  • L1_handler to receive bridged tokens from l1
  • Received message:
    • L2_recipient
    • Token amount
  • Checks the bridge is authorized
  • Mints/Unlocks tokens on L1 to the specified address

Flow diagram


An example flow for a token contract minted on L2 and later deployed on L1


Example flow of bridging a token to an L1 instance

Implementation example

An example implementation for both L1 and L2 can be found here:

The following contracts have been deployed to StarkNet and Goerli testnet to demonstrate functionality:

Example contracts

Contract Link
L2 ERC20 Contract Class L2 ERC20 Contract Class
L2 ERC20 Contract L2 ERC20 Contract
L1 ERC20 Goerli Bridge L1 ERC20 Goerli Bridge
L1 ERC20 Goerli Instance L1 ERC20 Goerli Instance

This implementation is only an example. To make this proposal concrete, both the L1 bridge and the L2 interface should be finalized and implemented by serious people :innocent:


I like the idea… ie having this would be great for us… but I can’t judge the technical aspect of things when it comes to the details of bridging


Thanks. The technical aspect can be changed. The implementation given here is more for reference.
The purpose was to get an idea of how many find this useful, and how much should this be pushed forward as a standard.


Can you please elaborate on how would you use it?


I really like the idea. I definitely think it would be useful, and especially in terms of security. The decoupling of bridging functionality and the logic of the token is a good thing.
+1 to make it an official proposal


Let’s try to push this forward.
I might need to make some modifications following recent changes of deployments, but keep the same idea


Congratulations @amanusk for your work. I believe your proposal would be very beneficial for the community and should be considered official.

During our last voice call, we discussed the possibility of extending it into a standard for a two-way bridge L1-L2 / L2-L1 for NFTs (ERC-721 and ERC-1155).
This option should be given careful consideration as soon as possible because it could alter your initial proposal, such as the use of the burn function.

Screenshot team and I would be willing to contribute to the development of this proposal.


Great to have you on board! :star_struck:
Indeed the considerations and requirements for ERC-721 and ERC-1155 can be different.

Let’s continue the discussion here.
We can use the repository to coordinate a reference implementation. If we want both L2->L1 and L1->L2 contracts following a similar idea, we would need Solidity contracts, so this can be advance regardless of Cairo 1 status.

Some points we can consider for ERC-721 implementation:

  • What other fields do we need to pass to the L1 bridge to bind the contracts?
    • Do we want to pass the URI
    • Which String library to use for it
    • Do we need a “updateURI” function to match both via a permissionless tx
  • Do we want to burn and mint or to lock and unlock
  • Do we want this to be implemented by the contracts itself, or to also provide a bridge implementation that mimics this functionality