Homepage | Docs | Developers
This project introduces two tokens: a base token (FRNT) and a yield-bearing token (wFRNT), implemented with an ERC-4626 vault. Both token contracts extend ERC-20F, a Fireblocks standard built on top of OpenZeppelin’s ERC20Upgradeable contract.
On the hub chain, the FrontierVault - an ERC-4626-compliant vault - manages the yield-bearing token. On the spoke chains, a standard ERC-20F implementation of wFRNT is deployed.
A FrontierAccessRegistry contract governs wallet permissions across both tokens. This contract is queried before any transfer, mint, or receive operation and may delegate additional checks to the Chainalysis sanctions list to enforce compliance.
Cross-chain token movement is enabled through adapters:
- For the yield-bearing token (
wFRNT), an OFT adapter (FrontierOFTAdapter) is deployed on the hub chain. It uses a lock/unlock mechanism to preserve the vault's total supply-based yield calculations. Burning tokens during cross-chain transfers is not allowed to maintain consistency in yield computation. - For the base token (
FRNT) and the spoke chain versions ofwFRNT, mint/burn adapters are used:FRNTMintAndBurnAdapterandwFRNTMintAndBurnAdapter(both inheritFrontierOFTAdapterMintAndBurn).
Scope:
contracts
├ FrontierERC20F.sol
├ FrontierOFTAdapter.sol
├ FrontierOFTAdapterMintAndBurn.sol
└ FrontierVault.solThe deployment order is really important:
- Deploy
DenyListon all chains of the mesh; - Deploy
FrontierERC20Fon all chains and set the deny list address (FrontierERC20F.accessRegistryUpdate(...)) for both base and yield bearing tokens; - Deploy
FrontierVaulton the hub chain and set the deny list address (FrontierVault.accessRegistryUpdate(...)); - Deploy
FrontierOFTAdapterMintAndBurnon all chains setting the address of the underlying token as token andminterBurner; - Grant minter and burner role to the adapter on the token contract on all chains;
- Deploy
FrontierOFTAdapteron the hub chain, setting asFrontierVaulttoken;
Some functions have specific access controls:
- Mint (
mint(...)): only minter role and only to active wallets - Burn (
burn(...)): only burner role - Transfer (
transfer(...)/transferFrom(...)): only from and to active wallets - Add/remove wallet to/from deny list (
accessListAdd(...)/accessListRemove(...)): only access list admin role - Add deny list registry to the token contract (
accessRegistryUpdate(...)): only contract admin role - Pause/unpause (
pause(...)/unpause(...)): only pauser role - Upgrade (
upgradeToAndCall(...)): only upgrader role - Salvage tokens (
salvageERC20(...)/salvageGas(...)): only salvage role
“Active wallets” means wallets that are not in the deny list.
You can call FrontierOFTAdapterMintAndBurn.send(...) as you would call a regular OFT contract.
In the case of cross-chain transfer where the recipient address on the destination chain is frozen, the transfer will go through even though the wallet is frozen (by calling mintToFrozenWallet function on MABA and transferToFrozenWallet on the OFT adapter). This is done so that the destination transaction doesn’t revert. The recipient won’t be able to use the received tokens anyway, since the wallet is frozen and it can’t transfer the tokens, even cross-chain.
cargo install solana-verify --git https://github.com/Ellipsis-Labs/solana-verifiable-buildsolana-verify verify-from-repo -um --program-id 3eZMk1HzqcbgBPjjyz9Hkd2QPxLDXCiVBx2syKUYT9oC <repo url> --library-name oft -- --config env.OFT_ID=\'3eZMk1HzqcbgBPjjyz9Hkd2QPxLDXCiVBx2syKUYT9oC\'
To see the verification status from the api, please run: curl https://verify.osec.io/status/3eZMk1HzqcbgBPjjyz9Hkd2QPxLDXCiVBx2syKUYT9oC | jq
If there are any failures, ensure the local and on chain program hashes match:
RUSTUP_TOOLCHAIN=nightly-2025-05-01 anchor build -v -e OFT_ID=3eZMk1HzqcbgBPjjyz9Hkd2QPxLDXCiVBx2syKUYT9oCsolana-verify get-executable-hash ./target/verifiable/oft.sosolana-verify get-program-hash --url "<mainnet rpc url>" 3eZMk1HzqcbgBPjjyz9Hkd2QPxLDXCiVBx2syKUYT9oC
The output from steps 2 and 3 should match.
The adapter is not given enough msg.value to pay for the transferring tokens. This will hard revert on the source chain with error InsufficientMessageValue.
If the to or from address is in the deny list, the transaction will revert
If the from address is in the deny list, the transaction will revert
If a caller address calls a function with role-base access control and does not have that specific role granted to itself, the transaction will revert.
- Populate .env file with
FIREBLOCKS_API_KEY=
FIREBLOCKS_PRIVATE_KEY=
FIREBLOCKS_VAULT_ACCOUNT_IDS=-
Update
consts/wire.tswith DVNs and enforced options as needed (see how it is done for existing chains) -
Update the
layerzero-mainnet.config.tswith the new chain (see how it is done for existing chains) TODO iron out solana wiring -- best ux is to have end user update this config file for both evm and solana
- Run
npx hardhat lz:oapp:wire --oapp-config layerzero-mainnet.config.ts