Skip to content

ERC20 OmniVault / Fraxtal omnistaking #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
dea310c
gitignore
Feb 10, 2025
b436ff3
bridge-lz
Feb 10, 2025
22ea91a
deployer scripts
Feb 10, 2025
1289746
vaults and iToken (with old oZepp libs)
Feb 10, 2025
5ad0d48
wip everything compiles with older oZepp
Feb 10, 2025
07c9358
fix tests for older oZepp
Feb 10, 2025
b254826
cleaning up
Feb 10, 2025
2c60d72
tidy up depl script
Feb 10, 2025
0d4872c
update network configs with fraxtal mainnet/testnet
Feb 10, 2025
aa1dca1
readme
Feb 10, 2025
0150954
readme for deployment scripts
Feb 11, 2025
43874b6
wip fix deloyer
Feb 11, 2025
4c3083f
fix stage2 and fraxferry bridge init
Feb 11, 2025
9db8137
readme and configs
Feb 11, 2025
1d49df6
wip prod pre-config
Feb 11, 2025
8030478
add L1 adapter deployment
Feb 11, 2025
0e431ad
wip full testnet deployment (w/o RF and lockbox)
Feb 11, 2025
2ab0c8a
wip
Feb 11, 2025
7d70c55
fix rebalancer default ChainID
Feb 11, 2025
c4c8d63
update readme and commented code
Feb 12, 2025
68391a7
comment cleanup
Feb 12, 2025
ee681b2
comments
Feb 12, 2025
16ee75b
bug
Feb 13, 2025
fc765db
xerc20 minter in the script
Feb 13, 2025
dc83f09
capital letter
Feb 13, 2025
c6d148d
fix dependency order
Feb 13, 2025
a6eca2d
remove cca dep
Feb 13, 2025
c00419e
minor improvements
mellaught Feb 14, 2025
0203126
minor fixes for InceptionToken
mellaught Feb 19, 2025
1eb78e6
upgrade script for insfrxETH
Feb 19, 2025
18ccaa3
deployed a new impl for insfrxETH
mellaught Feb 20, 2025
69fab70
LZ config scripts
Feb 21, 2025
0a084c7
Merge branch 'fraxtal-omnistaking' of github.com:inceptionlrt/smart-c…
Feb 21, 2025
ce821c8
fix l2 chainid
Feb 27, 2025
54ef5e9
misc scripts
Feb 27, 2025
d6a2eef
add a way to set LZ delegate to contract owner
Feb 28, 2025
f8ed420
adapter upgrade scripts
Feb 28, 2025
c549af0
deployed new impls for adapters
mellaught Mar 3, 2025
7da7ef2
Merge branch 'master' into fraxtal-omnistaking
Mar 5, 2025
1e5d2c8
audit
Jan 14, 2025
eb42ad4
audit readme
Jan 14, 2025
8fe7510
misc
Mar 5, 2025
c135226
more cleaning up
Mar 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,12 @@ typechain-types
# Hardhat files
cache
artifacts
projects/vaults/storageLayout
*.bin
*.abi

# Inception LRT custom ignore
*.tgz
/projects/tests/omnivault-rebalancer/contracts/
/projects/omnichain-deployer/contracts/

projects/vaults/.solhint.json
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
![OmniStaking Architecture](./OmniStaking_Architecture.jpg)

# OmniStaking

OmniStaking is a cross-chain staking system designed to maintain the balance of Inception Tokens across Layer 1 (L1) and multiple Layer 2 (L2) networks. By managing both data and ETH transfers between chains, OmniStaking ensures that the protocol maintains a consistent supply of Inception Tokens across all deployed chains, creating a unified and decentralized staking ecosystem.

## Overview

OmniStaking operates through a sequence of cross-chain communications involving several key contracts and components:

1. **InceptionOmniVault**: This contract initiates the transfer process. Operators (backend) interact with it to send either:

- **Data Messages**: For reporting asset balances, using `sendAssetsInfoToL1()`.
- **ETH Transfers**: For transferring ETH across chains, using `sendEthCrossChain()`.

2. **LZCrossChainAdapterL2**: This contract, specific to L2 chains, is responsible for processing and transmitting cross-chain messages. It receives calls from `InceptionOmniVault` and initiates cross-chain transfers using the LayerZero protocol.

3. **LZCrossChainAdapterL1**: After a specified delay (e.g., 7 days for mainnets or 20 minutes for testnets), messages or ETH transfers from L2 arrive at this L1 contract. Here, the contract:

- Decodes data messages.
- Relays the decoded information or ETH to the **NativeRebalancer**.

4. **NativeRebalancer**: This L1 contract aggregates data from all L2 chains. When data from each L2 is received, users can call `updateTreasuryData` on **NativeRebalancer**. This function recalculates the token supply, minting or burning tokens to maintain the invariant:

$$
\text{sum(Inception Tokens on L2s)} = \text{Inception Tokens on L1}
$$

## Message Flow

### L2 to L1 (Data and ETH Transfer)

1. **Data Transfer**:

- The operator calls `sendAssetsInfoToL1()` on **InceptionOmniVault**.
- This data message is forwarded to **LZCrossChainAdapterL2**, which encodes and sends the data across chains.

2. **ETH Transfer**:
- The operator initiates `sendEthCrossChain()` on **InceptionOmniVault**.
- **LZCrossChainAdapterL2** processes and sends the ETH transfer request to **LZCrossChainAdapterL1**.

### L1 Reception and Balancing

After the specified waiting period, **LZCrossChainAdapterL1** receives the cross-chain message or ETH transfer:

1. **Data Message**: **LZCrossChainAdapterL1** decodes the data and relays it to **NativeRebalancer**.
2. **ETH Transfer**: The ETH is directly forwarded as specified in the initial transfer request.

Once all L2 data has been received, users call `updateTreasuryData()` on **NativeRebalancer**, which mints or burns Inception Tokens on L1 as needed to ensure cross-chain token balance.

## Components

- **InceptionOmniVault**: Initiates L2 to L1 cross-chain transfers.
- **LZCrossChainAdapterL2**: Manages cross-chain messaging from L2 to L1.
- **LZCrossChainAdapterL1**: Receives and processes messages or ETH on L1.
- **NativeRebalancer**: Maintains the Inception Token invariant across chains by adjusting L1 supply.

## Invariant Guarantee

OmniStaking maintains the invariant:

$$
\text{sum(Inception Tokens on L2s)} = \text{Inception Tokens on L1}
$$

This ensures that the total supply of Inception Tokens is balanced across all chains in the OmniStaking ecosystem.
Binary file not shown.
16 changes: 16 additions & 0 deletions audits/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,19 @@ This folder consists of the audit reports performed on Inception Vaults smart co
| LST Vaults Release of V2, M2 EigenLayer contracts | All contracts are located in the `projects/vaults/contracts/Inception/` | Halborn | 01.05.2024 |
| LST Vaults Release of the Flash withdrawal feature | All contracts are located in the `projects/vaults/contracts/Inception/` | Halborn | 28.06.2024 |
| SymbioticVault, DiamondProxy for InceptionVaults | All contracts are located in the `projects/vaults/contracts/` | Halborn | 25.10.2024 |
| Omnistaking | `projects/bridge-lz/contracts/abstract/AbstractCrossChainAdapter.sol` | Salus | 08.11.2024 |
| | `projects/bridge-lz/contracts/abstract/AbstractCrossChainAdapterL1.sol` | | |
| | `projects/bridge-lz/contracts/abstract/AbstractCrossChainAdapterL2.sol` | | |
| | `projects/bridge-lz/contracts/abstract/AbstractLZCrossChainAdapter.sol` | | |
| | `projects/bridge-lz/contracts/LZCrossChainAdapterL1.sol` | | |
| | `projects/bridge-lz/contracts/LZCrossChainAdapterL2.sol` | | |
| | `projects/vaults/contracts/vaults/InceptionOmniVault.sol` | | |
| | `projects/vaults/contracts/assets-handler/InceptionAssetsHandler.sol` | | |
| | `projects/restaking-pool/contracts/NativeRebalancer.sol` | | |
| | `projects/restaking-pool/contracts/cToken.sol` | | |
| Fraxtal ERC20 Omnistaking | `projects/bridge-lz/contracts/abstract/AbstractFraxFerryERC20Adapter.sol` | Salus | 14.01.2025 |
| | `projects/bridge-lz/contracts/FerryAdapter/FraxFerryLZCrossChainAdapterL2.sol` | | |
| | `projects/vaults/contracts/rebalancer/ERC20Rebalancer.sol` | | |
| | `projects/vaults/contracts/rebalancer/ERC20RebalancerStorage.sol` | | |
| | `projects/vaults/contracts/vaults/InceptionERC20OmniVault.sol` | | |

47 changes: 47 additions & 0 deletions hh.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const CONFIG = {
mainnet: {
url: process.env.MAINNET_RPC || "https://rpc.ankr.com/eth",
chainId: 1,
eid: 30101,
gas: 8000000,
gasPrice,
accounts,
Expand All @@ -27,6 +28,34 @@ export const CONFIG = {
},
},
},
fraxtal: {
url: process.env.RPC_URL_FRAX_HOLESKY || "https://rpc.frax.com",
chainId: 252,
gas: 8000000,
gasPrice,
accounts,
eid: 30255,
verify: {
etherscan: {
apiKey: process.env.FRAXSCAN_API_KEY,
apiUrl: "https://api.fraxscan.com/api",
},
},
},
fraxHolesky: {
url: process.env.RPC_URL_FRAX_HOLESKY || "https://rpc.testnet.frax.com",
chainId: 2522,
gas: 8000000,
gasPrice,
accounts,
eid: 40255,
verify: {
etherscan: {
apiKey: process.env.FRAXSCAN_API_KEY,
apiUrl: "https://api-holesky.fraxscan.com/api",
},
},
},
local: {
url: process.env.LOCAL_RPC || "http://127.0.0.1:8545",
chainId: 1337,
Expand Down Expand Up @@ -84,6 +113,8 @@ export const CONFIG = {
etherscan: {
apiKey: {
holesky: "PP5CDPZBG6AF6FBGE9CJNYGCRYXYN549M1",
fraxHolesky: process.env.FRAXSCAN_API_KEY,
fraxtal: process.env.FRAXSCAN_API_KEY,
mainnet: process.env.ETHERSCAN_API_KEY,
},
customChains: [
Expand All @@ -95,6 +126,22 @@ export const CONFIG = {
browserURL: "https://holesky.etherscan.io",
},
},
{
network: "fraxHolesky",
chainId: 2522,
urls: {
apiURL: "https://api-holesky.fraxscan.com/api",
browserURL: "https://holesky.fraxscan.com",
},
},
{
network: "fraxtal",
chainId: 252,
urls: {
apiURL: "https://api.fraxscan.com/api",
browserURL: "https://fraxscan.com",
},
},
],
},
sourcify: {
Expand Down
22 changes: 22 additions & 0 deletions projects/bridge-lz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<details>


<summary>Deposit and (flash) withdrawal</summary>


![OmniStaking Architecture](./img/OmniStaking-Deposit-FWithdrawal.svg)


<summary>Sync to L1</summary>


![OmniStaking Architecture](./img/OmniStaking-SyncToL1.svg)


<summary>Token transfer to L1 via FraxFerry</summary>


![OmniStaking Architecture](./img/OmniStaking-FerryToL1.svg)


</details>
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {AbstractLZCrossChainAdapter} from "../abstract/AbstractLZCrossChainAdapter.sol";
import {AbstractCrossChainAdapterL2} from "../abstract/AbstractCrossChainAdapterL2.sol";
import {AbstractCrossChainAdapter} from "../abstract/AbstractCrossChainAdapter.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {AbstractFraxFerryERC20Adapter} from "../abstract/AbstractFraxFerryERC20Adapter.sol";
import {IAdapter} from "../interfaces/IAdapter.sol";
import {IFraxFerry} from "../interfaces/IFraxFerry.sol";

import {Origin} from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";

/**
* @title FraxFerryLZCrossChainAdapterL2
* @author InceptionLRT
* @dev Layer 2 adapter for LayerZero cross-chain communication, supporting ERC20 transfers and data messaging with Layer 1.
* This contract manages endpoint and chain ID mappings, enables quoting for cross-chain transactions, and provides functions for
* data transfer to L1.
*/
contract FraxFerryLZCrossChainAdapterL2 is
AbstractLZCrossChainAdapter,
AbstractCrossChainAdapterL2,
AbstractFraxFerryERC20Adapter,
Initializable,
Ownable2StepUpgradeable
{
uint32 private _l1ChainId;

modifier onlyOwnerRestricted()
override(AbstractCrossChainAdapter, AbstractLZCrossChainAdapter) {
_checkOwner();
_;
}

modifier onlyTargetReceiverRestricted() override {
require(
msg.sender == targetReceiver || msg.sender == owner(),
NotTargetReceiver(msg.sender)
);
_;
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() payable {
_disableInitializers();
}

function initialize(
address _token,
address _ferry,
address _endpoint,
address _delegate,
uint32 l1ChainId,
uint32[] memory _eIds,
uint256[] memory _chainIds
) public initializer {
__Ownable2Step_init();
__OAppUpgradeable_init(_endpoint, _delegate);
require(_eIds.length == _chainIds.length, ArraysLengthsMismatch());
_l1ChainId = l1ChainId;
token = IERC20(_token);
ferry = IFraxFerry(payable(_ferry));
for (uint256 i = 0; i < _eIds.length; i++)
setChainIdFromEid(_eIds[i], _chainIds[i]);
}

function setDestination(address _dest) external onlyOwnerRestricted {
require(_dest != address(0), errNullDestination());
erc20DestinationChain = _dest;
emit DestinationChanged(_dest);
}

function setFerry(address _ferry) external onlyOwnerRestricted {
require(_ferry != address(0), IFraxFerry.errNullFerry());
ferry = IFraxFerry(payable(_ferry));
emit IFraxFerry.FerryChanged(_ferry);
}

function quote(bytes calldata _payload, bytes memory _options)
external
view
override
returns (uint256)
{
return _quote(_l1ChainId, _payload, _options);
}

function sendDataL1(bytes calldata _payload, bytes memory _options)
external
payable
override
onlyTargetReceiverRestricted
returns (uint256)
{
return _sendCrosschain(_l1ChainId, _payload, _options);
}

function _lzReceive(
Origin calldata origin,
bytes32, /*_guid*/
bytes calldata,
address, /*_executor*/
bytes calldata /*_extraData*/
) internal virtual override {
uint256 chainId = getChainIdFromEid(origin.srcEid);
if (msg.value > 0) _handleCrossChainEth(chainId);
}

/// @dev This will allow TargetReceiver to recover ERC20 accidentally sent to the adapter itself.
// Tokens will be sent back to TargetReceiver (aka vault).
function recoverFunds()
external
override(AbstractCrossChainAdapter, IAdapter)
onlyOwnerRestricted
{
require(targetReceiver != address(0), TargetReceiverNotSet());
token.transfer(targetReceiver, token.balanceOf(address(this)));
}

// stubs for eth methods
function sendEthCrossChain(
uint256, /*_chainId */
bytes memory /* _options */
)
external
payable
override(AbstractLZCrossChainAdapter, IAdapter)
returns (uint256)
{
revert NotAllowedInThisAdapterType();
}

function quoteSendEth(
uint256, /* _chainId */
bytes memory /* _options */
)
external
pure
override(AbstractLZCrossChainAdapter, IAdapter)
returns (uint256)
{
revert NotAllowedInThisAdapterType();
}

function setDelegateToCurrentOwner() external onlyOwnerRestricted {
address delegate = owner();
endpoint.setDelegate(delegate);
emit LZDelegateSet(delegate);
}
}
Loading