Introduction
Sonic mainnet has recently launched, bringing a wave of developer activity as projects move to take advantage of its high-speed, low-cost, and scalable infrastructure. With full EVM compatibility, 10,000 TPS, and sub-second finality, Sonic provides an optimized environment for applications, supported by a robust incentive program for long-term growth.
Protocols migrating from other chains can leverage LayerZero’s battle-tested omnichain interoperability protocol to provide secure cross-chain messaging for their token migration.
With the Beets migration from Fantom to Sonic, the DAO voted to redefine our tokenomics from the ground up. Instead of relying on a traditional emission schedule, we implemented an emission management and budget system. You can read more about this here: Beets Emission System Proposal.
As we were transitioning to a fundamentally new system — one that required the ability to mint tokens quarterly based on DAO votes — we could not rely solely on a LayerZero bridged token. Instead, we deployed a new native ERC-20 token on Sonic with the necessary capabilities to support our revised tokenomics. To facilitate the migration, we implemented a migration contract that enables swaps between the LayerZero OFT on Sonic and the native BEETS token on Sonic.
The migration contract allows for bidirectional exchanges but also includes the flexibility to close one or both migration paths if the DAO decides to phase out the process. This ensures that the migration remains governance-controlled and adaptable to future changes.
This guide walks through the step-by-step process for deploying an OFT. In addition, if you want to migrate to a native Sonic token, setting up a migration contract to enable token exchanges.
Tip: Before proceeding, ensure you have:
- A deployed ERC-20 token on another blockchain (e.g., Ethereum, Fantom, Base, etc.).
- Access to LayerZero’s deployed contracts for Sonic.
- A funded wallet to cover deployment and transaction fees.
Step 1: Create an Omnichain Fungible Token (OFT) with LayerZero
Creating an Omnichain Fungible Token (OFT) using LayerZero allows your token to move securely between chains while maintaining interoperability. This step ensures that your token can be transferred between Sonic and the source chain as part of the migration process.
Steps:
- Clone the LayerZero OFT Contract: Use LayerZero’s OFT contract template as the base for your token.
- Modify the Contract for Your Token: Set parameters such as name, symbol, and decimals to match your existing token.
- Deploy the OFT Contract on the Source Chain: Use Hardhat, Foundry, or Remix to deploy your contract on Ethereum, Fantom, or another EVM chain.
- Configure LayerZero Endpoints for Sonic: Connect the source chain to Sonic by setting up LayerZero’s cross-chain messaging.
- Test Cross-Chain Transfers: Before deploying to mainnet, ensure that OFT transfers correctly between chains using a testnet environment.
Resources:
Step 2: Deploy a Native ERC-20 Token on Sonic
A native ERC-20 token on Sonic ensures full compatibility with the network’s DeFi ecosystem. This native token will be used in the migration process to replace the existing tokens on the source chain.
Steps:
- Write an ERC-20 Contract: Implement an ERC-20 token contract with necessary parameters such as name, symbol, decimals, and initial supply.
- Deploy the Contract on Sonic: Use a blockchain development framework such as Hardhat, Foundry, or Remixto deploy the contract on Sonic Mainnet.
- Verify the Contract on a Block Explorer: Once deployed, verify the contract using Sonic’s block explorer to enhance transparency and ease of interaction.
Example ERC-20 Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract SonicToken is ERC20 {
constructor() ERC20("Sonic Token", "STKN") {
_mint(msg.sender, 1_000_000 * 10 ** decimals());
}
}
Resources:
Step 3: Deploy and Configure the Migration Contract
The migration contract acts as the bridge between the OFT token and the native Sonic token. It allows users to swap their existing OFT for the newly deployed Sonic-native ERC-20 token.
Steps:
- Deploy the Migration Contract: Deploy the contract to Sonic Mainnet using Hardhat, Foundry, or Remix.
- Set the Token Addresses : Assign the correct OFT address (from Step 1) and the native Sonic token address(from Step 2).
- Seed the Contract with Liquidity: Ensure the migration contract holds a sufficient balance of native Sonic tokens to facilitate swaps
- Assign Admin Privileges: The deployer should be the admin, with control over migration toggles.
- Enable Migration: Once tested, enable migration for users.
Example Migration Contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SonicTokenMigrator is ReentrancyGuard {
IERC20 public immutable OPERATOKEN;
IERC20 public immutable SONICTOKEN;
address public immutable TREASURY;
bool public sonicToOperaEnabled = false;
bool public operaToSonicEnabled = false;
address public admin;
error UserBalanceInsufficient();
error MigratorBalanceInsufficient();
error MigrationDisabled();
error NotAdmin();
constructor(IERC20 _OPERATOKEN, IERC20 _SONICTOKEN, address _TREASURY) {
OPERATOKEN = _OPERATOKEN;
SONICTOKEN = _SONICTOKEN;
TREASURY = _TREASURY;
admin = msg.sender;
}
modifier onlyAdmin() {
require(msg.sender == admin, "NotAdmin");
_;
}
function exchangeOperaToSonic(uint256 amount) external nonReentrant {
require(operaToSonicEnabled, "MigrationDisabled");
require(OPERATOKEN.balanceOf(msg.sender) >= amount, "UserBalanceInsufficient");
require(SONICTOKEN.balanceOf(address(this)) >= amount, "MigratorBalanceInsufficient");
OPERATOKEN.transferFrom(msg.sender, address(this), amount);
SONICTOKEN.transfer(msg.sender, amount);
}
function exchangeSonicToOpera(uint256 amount) external nonReentrant {
require(sonicToOperaEnabled, "MigrationDisabled");
require(SONICTOKEN.balanceOf(msg.sender) >= amount, "UserBalanceInsufficient");
require(OPERATOKEN.balanceOf(address(this)) >= amount, "MigratorBalanceInsufficient");
SONICTOKEN.transferFrom(msg.sender, address(this), amount);
OPERATOKEN.transfer(msg.sender, amount);
}
function setAdmin(address _admin) external onlyAdmin {
admin = _admin;
}
function enableOperaToSonic(bool _toggle) external onlyAdmin {
operaToSonicEnabled = _toggle;
}
function enableSonicToOpera(bool _toggle) external onlyAdmin {
sonicToOperaEnabled = _toggle;
}
function withdrawOperaToken() external onlyAdmin {
OPERATOKEN.transfer(TREASURY, OPERATOKEN.balanceOf(address(this)));
}
function withdrawSonicToken() external onlyAdmin {
SONICTOKEN.transfer(TREASURY, SONICTOKEN.balanceOf(address(this)));
}
}
Resources:
Step 4: Build the User Flow for Token Migration
To facilitate a seamless migration process, a user-friendly UI should be created.
Source Chain Migration Flow:
- Check the user’s wallet balance for the existing token.
- Allow the user to approve for token transfer to the LayerZero contract.
- Call the LayerZero endpoint to bridge the tokens to Sonic using the correct parameters.
Sonic Migration Flow (If Native Token Migration needed):
- Check the user’s wallet balance for the bridged token.
- If the user has a balance, allow them to approve the token for transfer.
- Call the migration contract to exchange the bridged token for the native Sonic token.
Resources:
Step 5: Testing and Deployment
Before finalizing the migration, thoroughly test the contracts to ensure security, efficiency, and functionality.
Testing the Contracts:
- Unit Testing:
- Use Hardhat, Foundry, or a similar framework to write and run unit tests.
- Test all major functions, including token transfers, migration contract interactions, and LayerZero messaging.
- Ensure reentrancy protection and admin-only functions are properly restricted.
2. Deploy to a Testnet:
- Deploy the OFT contract, native ERC-20 token, and migration contract to a Sonic testnet.
- Perform mock transactions to validate the swap mechanism and liquidity management.
- Verify contract interactions with LayerZero’s testnet infrastructure.
3. Security Review:
- Run static analysis tools like Slither or MythX to detect vulnerabilities.
- Conduct a peer review with experienced developers.
- If possible, have a third-party security audit before mainnet deployment.
4. Mainnet Deployment:
- Deploy the final contracts on Sonic’s mainnet.
- Notify users about the migration process with clear guides and communication.
- Ensure that help channels and troubleshooting support are available
Resources:
Conclusion
By following this guide, you can successfully migrate your token to Sonic, ensuring seamless cross-chain functionality while maintaining interoperability across different blockchain networks.
If your project is planning to migrate to Sonic and needs help, reach out on the Beets Discord for guidance and support!