Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a3bd1ed
added Everclear components
mirooon Sep 22, 2025
389cfa8
changes
mirooon Sep 26, 2025
e0fe754
changes
mirooon Sep 28, 2025
b895f46
changes
mirooon Oct 6, 2025
da228b6
updates
mirooon Oct 7, 2025
cf184cd
merge with main
mirooon Oct 7, 2025
c977407
updates
mirooon Oct 7, 2025
4a1330e
updates
mirooon Oct 7, 2025
7d0848a
updates
mirooon Oct 8, 2025
b4fb890
updates
mirooon Oct 8, 2025
c50b30f
Merge branch 'main' into lf-14786-everclear
mirooon Oct 8, 2025
b5d19f9
Refactor EverclearFacet and IEverclearFeeAdapter: Updated SPDX licens…
mirooon Oct 8, 2025
2487ab8
Enhance EverclearFacet documentation and implementation: Updated the …
mirooon Oct 8, 2025
bad4c86
Implement native fee handling in EverclearFacet: Added nativeFee para…
mirooon Oct 8, 2025
1dc7857
Remove MessageHashUtils library and replace its functionality in Ever…
mirooon Oct 9, 2025
39e6abc
deployed EverclearFacet on staging
mirooon Oct 9, 2025
1ea5ed8
updates
mirooon Oct 9, 2025
9527bf1
Update deployment logs and demo script for EverclearFacet: Changed de…
mirooon Oct 9, 2025
b194cfc
Update demoEverclear script: Added transaction hash for bridging USDC…
mirooon Oct 9, 2025
010f82d
update demo script
mirooon Oct 10, 2025
9ea63b4
added tron script
mirooon Oct 10, 2025
533144a
added EverclearV2Facet
mirooon Oct 10, 2025
d50ab7b
Refactor EverclearFacet and EverclearV2Facet: Updated error handling …
mirooon Oct 10, 2025
f5ee499
Merge branch 'main' into lf-14786-everclear
mirooon Oct 10, 2025
f264b7b
added nativeFee in natspec for EverclearData
mirooon Oct 13, 2025
02661a7
remove old v1 in favor of v2
ezynda3 Nov 18, 2025
2364e1d
fix sig generation
ezynda3 Nov 18, 2025
7c9a130
fix
ezynda3 Nov 18, 2025
3e3a7c0
docs
ezynda3 Nov 21, 2025
cf23cc0
deploy to staging and create demo script
ezynda3 Nov 21, 2025
b4acd17
redeploy to staging update demo script
ezynda3 Nov 26, 2025
31f66ef
fix
ezynda3 Dec 1, 2025
11b3b3e
fixes
ezynda3 Dec 1, 2025
78bb769
Merge branch 'main' into lf-14786-everclear
ezynda3 Dec 1, 2025
5e45a9f
solana
ezynda3 Dec 1, 2025
b538ad2
fixes
ezynda3 Dec 2, 2025
76da9be
Add EverclearFacet entry to deployRequirements.json
ezynda3 Dec 2, 2025
e12a85e
Update EverclearFacet version to 1.0.0
ezynda3 Dec 2, 2025
06ff1a2
Implement positive slippage handling for Everclear swaps
ezynda3 Dec 2, 2025
5e6b6ff
Fix fee validation to prevent zero bridge amount
ezynda3 Dec 2, 2025
55a7b9d
Clarify fee handling comments in EverclearFacet
ezynda3 Dec 2, 2025
ea23948
Improve test code quality and add event checks
ezynda3 Dec 2, 2025
a8a6107
Clean up demo script and add amountOutMinMultiplier
ezynda3 Dec 2, 2025
a450a9d
Move Solana utility functions to demoScriptHelpers
ezynda3 Dec 2, 2025
e895636
Revert positive slippage handling changes
ezynda3 Dec 2, 2025
ca91f38
Implement positive slippage protection for Everclear swaps
ezynda3 Dec 2, 2025
dbdf8ef
Merge branch 'main' into lf-14786-everclear
ezynda3 Dec 2, 2025
8128542
Improve error comment to specify EVM OutOfFunds error
ezynda3 Dec 2, 2025
76c3cb3
Add all supported Everclear chains with FeeAdapter addresses
ezynda3 Dec 2, 2025
de1cb2b
Fix test signature format: use V2 FeeAdapter signature helpers
ezynda3 Dec 2, 2025
5ba1038
Update IEverclearFeeAdapter version to 1.0.0
ezynda3 Dec 2, 2025
bc47b72
updated demoEverclear.ts
mirooon Dec 3, 2025
96b1b32
Remove redundant variable assignments in positive slippage handling
ezynda3 Dec 3, 2025
e2394a6
Merge branch 'main' into lf-14786-everclear
ezynda3 Dec 3, 2025
d8179c3
add refundReceiver
ezynda3 Dec 10, 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
65 changes: 65 additions & 0 deletions config/everclear.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"mainnet": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"optimism": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"bsc": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"unichain": {
"feeAdapter": "0x877Fd0A881B63eBE413124EeE6abbCD7E82cf10b"
},
"polygon": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"zksync": {
"feeAdapter": "0xa537f0d027cBA1661dd1eB46fCd79030CD75A2cd"
},
"ronin": {
"feeAdapter": "0x5443DF583Cc039881Fe30A730310A4f43016df88"
},
"base": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"apechain": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"mode": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"arbitrum": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"avalanche": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"linea": {
"feeAdapter": "0xAa7ee09f745a3c5De329EB0CD67878Ba87B70Ffe"
},
"blast": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"scroll": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"taiko": {
"feeAdapter": "0x986962C73059e06303efaD1F4c61Ed4905Fc5431"
},
"berachain": {
"feeAdapter": "0x3c135048306b412Ad8F4375F6a8CBe94b5D56184"
},
"gnosis": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"mantle": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"sonic": {
"feeAdapter": "0xd0185bfb8107c5b2336bC73cE3fdd9Bfb504540e"
},
"ink": {
"feeAdapter": "0x877Fd0A881B63eBE413124EeE6abbCD7E82cf10b"
}
}
28 changes: 28 additions & 0 deletions deployments/_deployments_log_file.json
Original file line number Diff line number Diff line change
Expand Up @@ -46803,6 +46803,34 @@
}
}
},
"EverclearFacet": {
"arbitrum": {
"staging": {
"1.0.0": [
{
"ADDRESS": "0x508DEA1EdbE8F9D019d36B7Ba67f0EF8d8FBf35f",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2025-12-10 15:21:42",
"CONSTRUCTOR_ARGS": "0x000000000000000000000000d0185bfb8107c5b2336bc73ce3fdd9bfb504540e",
"SALT": "",
"VERIFIED": "true",
"ZK_SOLC_VERSION": ""
}
],
"2.0.0": [
{
"ADDRESS": "0xed26083ffc27AEcf9e958b81B51340076392Dc10",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2025-11-25 17:23:51",
"CONSTRUCTOR_ARGS": "0x000000000000000000000000d0185bfb8107c5b2336bc73ce3fdd9bfb504540e",
"SALT": "4242424242",
"VERIFIED": "false",
"ZK_SOLC_VERSION": ""
}
]
}
}
},
"UnitFacet": {
"plasma": {
"staging": {
Expand Down
18 changes: 11 additions & 7 deletions deployments/arbitrum.diamond.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,21 +185,25 @@
"Name": "DexManagerFacet",
"Version": "1.0.1"
},
"0x1A3c27FC0abbf9AA53F9c5CA89dE46D6CD1e5C7c": {
"Name": "LiFiIntentEscrowFacet",
"Version": "1.0.0"
},
"0xed26083ffc27AEcf9e958b81B51340076392Dc10": {
"Name": "",
"Version": ""
"Name": "EverclearFacet",
"Version": "1.0.0"
},
"0xb3B9C1d75bCc95bcD07F0c45c50A72A513FF8515": {
"Name": "LiFiIntentEscrowFacet",
"Version": "1.0.0"
},
"0xD9C95865a8a25614B4bbb77b1b55fa0bF207D7c2": {
"Name": "",
"Version": ""
},
"0x9ae70675f7d99231A6f917CA36F45c03a17F0417": {
"Name": "PolymerCCTPFacet",
"Version": "1.0.0"
},
"0x508DEA1EdbE8F9D019d36B7Ba67f0EF8d8FBf35f": {
"Name": "EverclearFacet",
"Version": "1.0.0"
}
},
"Periphery": {
Expand All @@ -219,4 +223,4 @@
"TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70"
}
}
}
}
1 change: 1 addition & 0 deletions deployments/arbitrum.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"Patcher": "0x18069208cA7c2D55aa0073E047dD45587B26F6D4",
"WhitelistManagerFacet": "0x603f0c31B37E5ca3eA75D5730CCfaBCFF6D17aa3",
"RelayDepositoryFacet": "0x004E291b9244C811B0BE00cA2C179d54FAA5073D",
"EverclearFacet": "0x508DEA1EdbE8F9D019d36B7Ba67f0EF8d8FBf35f",
"PolymerCCTPFacet": "0x9ae70675f7d99231A6f917CA36F45c03a17F0417",
"LiFiIntentEscrowFacet": "0xb3B9C1d75bCc95bcD07F0c45c50A72A513FF8515"
}
152 changes: 152 additions & 0 deletions docs/EverclearFacet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Everclear Facet

## How it works

The Everclear Facet enables cross-chain token bridging through the Everclear protocol, which uses a Spoke and Hub model to transport intents and settlements between supported domains. The facet interacts with an EverclearFeeAdapter contract that handles fee collection and signature verification before forwarding intents to the Everclear protocol.

Everclear uses an intent-based architecture where users create intents that specify their desired cross-chain transfers. These intents are then matched and settled through a netting mechanism that optimizes liquidity across chains. The protocol supports both EVM and non-EVM destination chains.

```mermaid
graph LR;
D{LiFiDiamond}-- DELEGATECALL -->EverclearFacet;
EverclearFacet -- CALL --> FeeAdapter(EverclearFeeAdapter);
FeeAdapter -- CALL --> E(Everclear Protocol)
```

## Public Methods

- `function startBridgeTokensViaEverclear(BridgeData calldata _bridgeData, EverclearData calldata _everclearData)`
- Simply bridges tokens using everclear
- `swapAndStartBridgeTokensViaEverclear(BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, everclearData memory _everclearData)`
- Performs swap(s) before bridging tokens using everclear

## Everclear Specific Parameters

The methods listed above take a variable labeled `_everclearData`. This data is specific to Everclear and is represented as the following struct type:

```solidity
/// @param receiverAddress The address of the receiver (bytes32 for non-EVM chains)
/// @param nativeFee The native fee amount (in native tokens, e.g., ETH)
/// @param outputAsset The address of the output asset on destination chain (bytes32 format)
/// @param amountOutMin The minimum amount out on destination chain
/// @param ttl The time to live for the intent (in seconds)
/// @param data Additional data for the intent (typically empty)
/// @param fee The protocol fee amount (in input token units)
/// @param deadline The deadline timestamp for the fee signature
/// @param sig The signature from the fee signer authorizing the fee
/// @param refundReceiver Address that will receive refunds from positive slippage
struct EverclearData {
bytes32 receiverAddress;
uint256 nativeFee;
bytes32 outputAsset;
uint256 amountOutMin;
uint48 ttl;
bytes data;
uint256 fee;
uint256 deadline;
bytes sig;
address refundReceiver;
}
```

### Fee Structure

The Everclear protocol uses a signed fee mechanism where:

- The `fee` is deducted from the bridge amount and collected separately
- The `nativeFee` (if non-zero) must be sent as msg.value and is used for cross-chain messaging costs
- The `sig` parameter contains an EIP-191 signature over the four parameters: `abi.encode(fee, nativeFee, inputAsset, deadline)`
- The signature must be created by the authorized fee signer in the EverclearFeeAdapter
- The `deadline` must be greater than or equal to the current block timestamp

### V2 Changes

- **Parameter Change**: `maxFee` (uint24) has been replaced with `amountOutMin` (uint256)
- **Native Fee Support**: Added `nativeFee` parameter for cross-chain messaging costs

### Chain Support

- **EVM Chains**: For EVM destination chains, `receiverAddress` must match `bridgeData.receiver` when converted to bytes32
- **Non-EVM Chains**: Set `bridgeData.receiver` to `NON_EVM_ADDRESS` and provide the actual receiver in `receiverAddress`

### Refund Receiver

The `refundReceiver` parameter specifies the address that will receive any positive slippage from source swaps. When using `swapAndStartBridgeTokensViaEverclear`, if the swap results in more tokens than expected, the excess amount is sent to the `refundReceiver` instead of being included in the bridge. This is necessary because Everclear's signature validation includes the original bridge amount, preventing adjustment of the amount sent to the protocol.

## Error Conditions

The facet will revert with specific errors in the following cases:

- `InvalidConfig()`: Constructor called with zero address for fee adapter
- `InvalidCallData()`: `outputAsset` is bytes32(0), `refundReceiver` is address(0), or bridge amount is less than or equal to fee
- `InvalidNonEVMReceiver()`: Non-EVM bridging with `receiverAddress` as bytes32(0)
- `InvalidReceiver()`: EVM bridging where `bridgeData.receiver` doesn't match `everclearData.receiverAddress`
- Standard LiFi validation errors for invalid bridge data

## Swap Data

Some methods accept a `SwapData _swapData` parameter.

Swapping is performed by a swap specific library that expects an array of calldata to can be run on various DEXs (i.e. Uniswap) to make one or multiple swaps before performing another action.

The swap library can be found [here](../src/Libraries/LibSwap.sol).

## LiFi Data

Some methods accept a `BridgeData _bridgeData` parameter.

This parameter is strictly for analytics purposes. It's used to emit events that we can later track and index in our subgraphs and provide data on how our contracts are being used. `BridgeData` and the events we can emit can be found [here](../src/Interfaces/ILiFi.sol).

## Getting Sample Calls to interact with the Facet

In the following some sample calls are shown that allow you to retrieve a populated transaction that can be sent to our contract via your wallet.

All examples use our [/quote endpoint](https://apidocs.li.fi/reference/get_quote) to retrieve a quote which contains a `transactionRequest`. This request can directly be sent to your wallet to trigger the transaction.

The quote result looks like the following:

```javascript
const quoteResult = {
id: '0x...', // quote id
type: 'lifi', // the type of the quote (all lifi contract calls have the type "lifi")
tool: 'everclear', // the bridge tool used for the transaction
action: {}, // information about what is going to happen
estimate: {}, // information about the estimated outcome of the call
includedSteps: [], // steps that are executed by the contract as part of this transaction, e.g. a swap step and a cross step
transactionRequest: {
// the transaction that can be sent using a wallet
data: '0x...',
to: '0x...',
value: '0x00',
from: '{YOUR_WALLET_ADDRESS}',
chainId: 100,
gasLimit: '0x...',
gasPrice: '0x...',
},
}
```

A detailed explanation on how to use the /quote endpoint and how to trigger the transaction can be found [here](https://docs.li.fi/products/more-integration-options/li.fi-api/transferring-tokens-example).

**Hint**: Don't forget to replace `{YOUR_WALLET_ADDRESS}` with your real wallet address in the examples.

### Cross Only

To get a transaction for a transfer from 30 USDC.e on Avalanche to USDC on Binance you can execute the following request:

```shell
curl 'https://li.quest/v1/quote?fromChain=AVA&fromAmount=30000000&fromToken=USDC&toChain=BSC&toToken=USDC&slippage=0.03&allowBridges=everclear&fromAddress={YOUR_WALLET_ADDRESS}'
```

### Swap & Cross

To get a transaction for a transfer from 30 USDT on Avalanche to USDC on Binance you can execute the following request:

```shell
curl 'https://li.quest/v1/quote?fromChain=AVA&fromAmount=30000000&fromToken=USDT&toChain=BSC&toToken=USDC&slippage=0.03&allowBridges=everclear&fromAddress={YOUR_WALLET_ADDRESS}'
```

## Additional Resources

- [Everclear Protocol Documentation](https://docs.everclear.org/developers/fundamentals)
- [Everclear API Reference](https://docs.everclear.org/developers/api)
Loading
Loading