Ethereum: Prove on L1 that an ERC20 transfer event happened on L2
Checking ERC20 transfer events on L1: Solidity example
In this article, we will look at how to verify ERC20 transfer events on the Ethereum Layer 2 (L2) network using the Solidity smart contract. Suppose that Alice transferred 100 USDC to Bob in transaction T of block B.
Problem: L1-Ethic transactions on L2
When transferring assets between different blockchains, for example from L1 to L2, the transaction is executed on L1 and then propagated to L2 through a series of Etherscan transactions. However, this process can be inefficient due to network latency and gas costs.
The ERC20 standard, which regulates the transfer of tokens between accounts, has been updated to include support for L1-ETH transfers. However, verifying these events remains a challenge due to the decentralized nature of blockchain networks.
Our solution: the Solidity contract
We will create a simple smart contract on Solidity that will demonstrate how to verify an ERC20 to L1 transfer event using the L2 network (Arbitrum One or Optimism). The contract will track all transactions involving Bob and identify those that have certain conditions, for example, the sender is Alice, and the recipient transferred 100 USDC.
`solidity
pragma solidity ^0.8.0;
import "
contract L1ToL2Verifier {
// Matching accounts with their transaction history
mapping(address => TransactionHistory) public transaction;
struct TransactionHistory {
uint256 blockNumber;
address fromAddress;
address toAddress;
uint256 amount;
}
event TransactionCreated(address indexed sender, address indexed receiver, uint256 amount);
event TransferEvent(address indexed from, address indexed in, uint256 sum);
function createTransactionHistory() public pure returns (TransactionHistory) {
return TransactionHistory(block.timestamp, msg.sender, 0, 100 10*18);
}
function sendTransaction(address _recipient, uint256 _amount) public payable {
// Create a transaction history for the sender
transactions[msg.sender].blockNumber = block.number;
transactions[msg.sender].fromAddress = _recipient;
// Update the recipient's transaction history
Transactions[_recipient].amount += _amount;
}
function verifyTransactionEvent(address _sender, address _recipient) public returns (bool) {
// Get the sender and recipient of the transfer from the L1 account history
TransactionHistory sender = Transactions[msg.sender];
TransactionHistory recipient = Transactions[_recipient];
// Check if the sender is Alice
require(sender.fromAddress == "0x...", "Invalid sender");
// Check if the recipient transferred 100 USDC
require(recipient.amount >= 100 10*18, "Recipient did not transfer enough tokens");
// Create a new event for the translation to occur (not shown in this example)
emit TransferEvent(sender.fromAddress, _recipient, 100 10*18);
return true;
}
}
`
Explanation
This Solidity contract consists of three main functions:
- createTransactionHistory
: Creates a new transaction history for the account.
- sendTransaction
: sends a transaction from one address to another and updates their transaction history.
- verifyTransactionEvent`: verifies whether a specific transfer event has occurred in the L1 network (Ethereum) using the provided sender and receiver addresses.
The contract uses a mapping to store all transactions involving Bob, including his transaction history.
Deployment
To deploy this contract, you need:
- Set up an Ethereum faucet account.
2.