Contracts Modifications
This section describes the contract modifications based on Optimism's Custom Gas Token to enable ERC-20 assets to function as native tokens on Layer 2. Thanos’ L2 Native Token Bridge allows seamless gas fee payments and transaction handling with tokens other than ETH to expand users' flexibility and utility. Through this enhancement, users can interact with L2 without ETH to expand token utility within the ecosystem.
Below are some points to keep in mind about the Optimism smart contracts’s design.
OptimismPortal
contract provides low level APIs for communication between L1 and L2. It is implemented as proxy and connected toOptimismPortal2
that supported fault proof.L1CrossDomainMessenger
contract handles cross-chain messages.L1StandardBridge
andL2StandardBridge
contracts provide high-level APIs for bridging tokens cross-chain.The L1 token when depositing, every kind of supported tokens are locked in
L1StandardBridge
except L2 native token is locked inOptimismPortal
The L2 Native Token Bridge incorporates additional asset transfer functionality, including transferFrom
and approve
logic, to enable withdrawals of ERC20-based native tokens. It also enhances message relay logic for better ERC20 compatibility and addresses potential security vulnerabilities in relaying cross-chain message.
In this section, we'll compare the differences between Optimism's Custom Gas Token and Thanos’ L2 Native Token Bridge. For ease of description, we'll use the following tags.
[New]: New features in L2 Native Token Bridge
[Modified]: Modifications in L2 Native Token Bridge compared to Optimism's Custom Gas Token
[Deleted]: Features removed from the L2 Native Token Bridge that were present in Optimism's Custom Gas Token
SystemConfig
In Thanos, the L1 token address of the corresponding L2 native token, called nativeTokenAddress
, is added to the storage. This signals to other contracts that this L1 token will serve as the native currency on L2.
Following contracts are updated to be aware of L2 native token using the nativeTokenAddress
:
OptimismPortal
L1CrossDomainMessenger
L1StandardBridge
[New] nativeTokenAddress()
nativeTokenAddress()
nativeTokenAddress
is a new view function that returns the L1 token address corresponding to the L2 native token. OptimismPortal
, L1CrossDomainMessenger
and L1StandardBridge
uses this to be aware of L2 native token.
OptimismPortal
[Modified] receive
receive
This function MUST revert because depositing ETH is not supported; only deposits of the L2 native token are allowed.
[Modified] depositTransaction
depositTransaction
Optimism’s Custom Gas Token uses depositTransaction
to send a message to L2 with ETH, and msg.value
determines how much ETH to mint on L2. On the other hand, L2 Native Token Bridge only supports L2 native tokens, so a separate calldata _mint
is used to specify how much L2 native token to mint on L2.
The differences in the interface are shown below.
Optimism
Thanos
Parameters
NameTypeDescription_to
address
The target of the deposit transaction
_mint
uint256
The amount of token to deposit
_value
uint256
The value of the deposit transaction, used to transfer native asset that is already on L2 from L1
_gasLimit
uint64
The gas limit of the deposit transaction
_isCreation
bool
Signifies the
_data
should be used withCREATE
_data
bytes
The calldata of the deposit transaction
This function is is not payable and It cannot be called inside the OptimismPortal
, so this function should be declared as an external function in Thanos. It does not include the modifier metered(gasLimit)
, however the logic of metered*(*gasLimit)
MUST be kept in the internal _depositTransaction
() function.
To deposit L2 native tokens, users MUST first approve
the address of OptimismPortal
so that OptimismPortal
can collect users‘ L2 native token. And it sends L2 native token from the _sender
address to the OptimismPortal
.
[Modified] finalizeWithdrawalTransaction
finalizeWithdrawalTransaction
It MUST call approve
to allow the target to transfer the L2 native token. The target MUST then use transferFrom
to receive the withdrawn token from OptimismPortal
.
If
_tx.data.length != 0
, theapprove
function is called to grant_tx.target
permission to access the amount specified by_tx.value
. Otherwise, if_tx.data.length == 0
, the token is directly transferred to_tx.target
usingsafeTransfer
.
The target address must not be the same as the L2 native token's address.
If
_tx.data.length != 0
and_tx.value
is non-zero, the token allowance is reset to 0, removing any remaining allowance. This security measure prevents potential vulnerabilities. It prevents spenders from using the same allowance multiple times or exploiting reentrancy attacks to gain additional benefits.
[New] onApprove
onApprove
This function is a callback that is triggered when an ERC20 token's approveAndCall
that extends the functionality of ERC20 tokens, allowing certain logic to be executed simultaneously with the token's allowance. This feature is primarily used to simplify interactions with smart contracts and improve the user experience. onApprove
is used to perform token transfers or handle specific tasks. It is only used with a chosen L2 native token that supports approveAndCall
(e.g., TON)
Parameters:
NameTypeDescription_owner
address
address of the account that called
approveAndCall
_amount
uint256
The amount of token to deposit
_data
bytes
The calldata of the deposit transaction
[New] unpackOnApproveData
unpackOnApproveData
This function decodes a bytes calldata input into its predefined structure using low-level assembly. It validates the input length and efficiently extracts the required components: target address, value, gas limit, and message.
Parameter: The function takes a single parameter
_data
, which is a bytes calldata.Return: It returns four values
_to
: The target address._value
: The value to be used in the transaction._gasLimit
: The gas limit for the transaction._message
: The remaining data to be used as a message.
Data Layout:
The first 20 bytes represent the target address (
_to
).The next 32 bytes represent the value (
_value
).The following 4 bytes represent the gas limit (
_gasLimit
).The rest of the data is the message (
_message
)
CrossDomainMessenger
[New] sendNativeTokenMessage
sendNativeTokenMessage
This function is nearly identical to sendMessage
, except it works with the L2 native token instead of ETH. The interface differences are outlined below.
Optimism
Thanos
Parameters
NameTypeDescription_target
address
address of the account that called
approveAndCall
_amount
uint256
The amount of token to deposit
_message
bytes
The calldata of the deposit transaction
_minGasLimit
uint32
Minimum gas limit that the message can be executed with.
This function MUST not be payable. Senders must first
approve
theL1CrossDomainMessenger
address, allowing it to usetransferFrom
to collect the L2 native token.L1CrossDomainMessenger
MUST approve address of the OptimismPortal, so that theOptimismPortal
can usetransferFrom
to collectL1CrossDomainMessenger
’s L2 native token whendepositTransaction
is called.
[Modified] relayMessage
relayMessage
This function securely relays messages, ensuring they are not replayed, have sufficient gas, and are sent to safe target addresses. The target MUST NOT be the address of the L2 native token and MUST revert if CALLVALUE
is not zero.
It verifies whether the message originates from the other messenger. If so, it confirms the message hasn't previously failed and transfers the value if necessary. If not, it ensures the message can be replayed.
It MUST include logic to approve
the target, allowing the target to use transferFrom
to collect the L2 native token from L1CrossDomainMessenger
when executing the message.
Thanos
Optimism
[New]onApprove
onApprove
This function is only used with a chosen L2 native token that supports approveAndCall
. Users can conveniently use sendNativeTokenMessage
by leverage this function.
Parameters:
NameTypeDescription_owner
address
address of the account that called
approveAndCall
_amount
uint256
The amount of token to deposit
_data
bytes
The calldata of the deposit transaction
[New] unpackOnApproveData
unpackOnApproveData
This function efficiently unpacks and retrieves structured data from a bytes calldata input. It's particularly useful for handling complex data structures passed as a single bytes parameter, such as in the approveAndCall
pattern.
Parameter: a single parameter
_data
, which is a bytes calldata.Return: It returns three values:
_to
: The target address to which the message is intended._minGasLimit
: The minimum gas limit required for executing the message._message
: The remaining data to be used as a message.
Data Layout:
The first 20 bytes represent the target address (
_to
).The next 4 bytes represent the minimum gas limit (
_minGasLimit
).The rest of the data is the message (
_message
).
StandardBridge
[New] bridgeNativeToken
bridgeNativeToken
This function is responsible to send L2 native token to the sender’s address on the other layer. The sender MUST be an EOA (Externally Owned Account). In L1StandardBridge
, Users have to approve the address of L1CrossDomainMessenger
first.
Parameters:
_amount
uint256
amount of deposited L2 native address
_minGasLimit
uint32
minimum gas limit for bridging other layer
_extraData
bytes
optional data to forward to L2
[New] finalizeBridgeNativeToken
finalizeBridgeNativeToken
This function is used for finalizing withdrawal native token messages from other layer. The message for calling this function MUST be triggered from other bridge.
Parameters
_from
address
address of sender
_to
address
address of receiver
_amount
uint256
amount of L2 native token being bridged
_extraData
bytes
optional data to be sent with the transaction
Difference between L1 and L2 bridge: Token Transfer
L1: Uses
safeTransferFrom
andsafeTransfer
for token movement, which is necessary for ERC20 tokensL2: Uses
SafeCall.call
to handle the transfer of native tokens.
[New] onApprove
& unpackOnApproveData
onApprove
& unpackOnApproveData
These functions are only used with L2 native token that supports
approveAndCall
. There are Implemented with the same logic asCrossDomainMessenger
.
[New] withdrawNativeToken
withdrawNativeToken
This function initiates a withdrawal L2 native token form L2 to L1 in L2StandardBridge
. Senders of this function must be EOA.
Parameters
_minGasLimit
uint32
minimum gas limit for relaying this message successfully on L1
_extraData
bytes
extra data that attached to the withdrawal transaction
[Modified]receive
receive
Users can deposit ETH by sending it directly to the L1StandardBridge
. To withdraw L2 native tokens, they should send them to the L2StandardBridge
. In both cases, senders MUST be externally owned accounts (EOAs).
[Modified] bridgeETH
bridgeETH
This function is responsible for initiating the bridging of ETH between layers. Senders MUST be EOA.
Difference between L1 and L2 bridge: Value Handling
L1: Checks that msg.value is non-zero and matches the
_amount
, updating the deposit balance.L2: Burns the ETH from the sender's balance using the
OptimismMintableERC20
interface.
[Modified] bridgeERC20
bridgeERC20
This function sends ERC20 tokens to a receiver's address on the other layer. It can execute with msg.value
set to zero, as it is designed to handle ERC20 tokens and does not involve any ETH specific logic. It MUST revert if using L2 native token. Senders MUST be EOA.
[Modified] finalizeBridgeETH
finalizeBridgeETH
This function is responsible for finalizing the bridging of ETH from one chain to another.
Difference between L1 and L2 bridge: Transfer vs Minting
L1: Transfers ETH using
SafeCall.call
, ensuring the recipient receives the ETH directly.L2: Mints
OptimismMintableERC20
tokens representing ETH to the recipient, as L2 typically uses token representations for assets.
[Modified] finalizeBridgeERC20
finalizeBridgeERC20
This function is responsible for completing the bridging process of ERC20 tokens from one chain to another. So _localToken
MUST not be the address of ETH or the address of an ERC20 token that does not follow OptimismMintableERC20
.
[Modified] withdraw
withdraw
This function initiates the withdrawal of assets from L2 to L1. L2 Native token Bridge distinguishes between L2 native token and ERC20 and includes additional validation on msg.value
for more secure usage. The caller of this function must be an EOA.
[Deleted] depositETH
, depositETHTo
, depositERC20
, depositERC20To
depositETH
, depositETHTo
, depositERC20
, depositERC20To
These functions are maintained for backward compatibility with high-level APIs by the Optimism team, ensuring that existing dApps on Optimism don't require updates when the chain is upgraded. However, as Thanos is a new chain without any running dApps, there's no need to retain these functions.
Last updated