-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #945 from lifinance/implement-glacis-LF-11761
LF-11761 - Implement glacis [GlacisFacet v1.0.0,IGlacisAirlift v1.0.0]
- Loading branch information
Showing
16 changed files
with
1,079 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"important": "these values are test deployments only. We need to update this file when Glacis has deployed their final versions", | ||
"arbitrum": { | ||
"airlift": "0xD9E7f6f7Dc7517678127D84dBf0F0b4477De14E0" | ||
}, | ||
"optimism": { | ||
"airlift": "0xdEedFc11fCd2bC3E63915e8060ec48875E890BCB" | ||
}, | ||
"base": { | ||
"airlift": "0x30095227Eb6d72FA6c09DfdeFFC766c33f7FA2DD" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# Glacis Facet | ||
|
||
## How it works | ||
|
||
The Glacis Facet works by forwarding calls to the [GlacisAirlift](https://github.com/glacislabs/airlift-evm/blob/main/src/facets/GlacisAirliftFacet.sol) core contract on the source chain. Glacis Airlift serves as a unified interface for facilitating token bridging across various native token bridging standards, such as those employed by Axelar, LayerZero, and Wormhole. While these standards may leverage General Message Passing protocols (GMPs), the primary focus of Glacis Airlift lies in enabling seamless interaction with the token bridging mechanisms themselves. | ||
|
||
```mermaid | ||
graph LR; | ||
D{LiFiDiamond}-- DELEGATECALL -->GlacisFacet; | ||
GlacisFacet -- CALL --> C(Glacis) | ||
``` | ||
|
||
## Public Methods | ||
|
||
- `function startBridgeTokensViaGlacis(BridgeData calldata _bridgeData, GlacisData calldata _glacisData)` | ||
- Simply bridges tokens using glacis | ||
- `swapAndStartBridgeTokensViaGlacis(BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, glacisData memory _glacisData)` | ||
- Performs swap(s) before bridging tokens using glacis | ||
|
||
## glacis Specific Parameters | ||
|
||
The methods listed above take a variable labeled `_glacisData`. This data is specific to glacis and is represented as the following struct type: | ||
|
||
```solidity | ||
/// @param refundAddress The address that would receive potential refunds on source chain | ||
/// @param nativeFee The fee amount in native token required by the Glacis Airlift | ||
struct GlacisData { | ||
address refundAddress; | ||
uint256 nativeFee; | ||
} | ||
``` | ||
|
||
## 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: 'glacis', // 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=glacis&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=glacis&fromAddress={YOUR_WALLET_ADDRESS}' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import { getContract, parseUnits, Narrow, zeroAddress, parseEther } from 'viem' | ||
import { randomBytes } from 'crypto' | ||
import dotenv from 'dotenv' | ||
import config from '../../config/glacis.json' | ||
import erc20Artifact from '../../out/ERC20/ERC20.sol/ERC20.json' | ||
import glacisFacetArtifact from '../../out/GlacisFacet.sol/GlacisFacet.json' | ||
import { GlacisFacet, ILiFi } from '../../typechain' | ||
import airliftArtifact from '../../out/IGlacisAirlift.sol/IGlacisAirlift.json' | ||
|
||
import { SupportedChain } from './utils/demoScriptChainConfig' | ||
import { | ||
ensureBalance, | ||
ensureAllowance, | ||
executeTransaction, | ||
setupEnvironment, | ||
getConfigElement, | ||
zeroPadAddressToBytes32, | ||
} from './utils/demoScriptHelpers' | ||
|
||
dotenv.config() | ||
|
||
// #region ABIs | ||
|
||
const ERC20_ABI = erc20Artifact.abi as Narrow<typeof erc20Artifact.abi> | ||
const GLACIS_FACET_ABI = glacisFacetArtifact.abi as Narrow< | ||
typeof glacisFacetArtifact.abi | ||
> | ||
const AIRLIFT_ABI = airliftArtifact.abi as Narrow<typeof airliftArtifact.abi> | ||
|
||
// #endregion | ||
|
||
dotenv.config() | ||
|
||
async function main() { | ||
// === Set up environment === | ||
const srcChain: SupportedChain = 'arbitrum' | ||
const destinationChainId = 10 | ||
|
||
const { | ||
client, | ||
publicClient, | ||
walletAccount, | ||
lifiDiamondAddress, | ||
lifiDiamondContract, | ||
} = await setupEnvironment(srcChain, GLACIS_FACET_ABI) | ||
const signerAddress = walletAccount.address | ||
|
||
// === Contract addresses === | ||
const SRC_TOKEN_ADDRESS = | ||
'0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91' as `0x${string}` | ||
const AIRLIFT_ADDRESS = getConfigElement(config, srcChain, 'airlift') | ||
|
||
// === Instantiate contracts === | ||
const srcTokenContract = getContract({ | ||
address: SRC_TOKEN_ADDRESS, | ||
abi: ERC20_ABI, | ||
client, | ||
}) | ||
|
||
const airliftContract = getContract({ | ||
address: AIRLIFT_ADDRESS, | ||
abi: AIRLIFT_ABI, | ||
client, | ||
}) | ||
|
||
const srcTokenName = (await srcTokenContract.read.name()) as string | ||
const srcTokenSymbol = (await srcTokenContract.read.symbol()) as string | ||
const srcTokenDecimals = (await srcTokenContract.read.decimals()) as bigint | ||
const amount = parseUnits('1', Number(srcTokenDecimals)) | ||
|
||
console.info( | ||
`Bridge ${amount} ${srcTokenName} (${srcTokenSymbol}) from ${srcChain} --> Optimism` | ||
) | ||
console.info(`Connected wallet address: ${signerAddress}`) | ||
|
||
await ensureBalance(srcTokenContract, signerAddress, amount) | ||
|
||
await ensureAllowance( | ||
srcTokenContract, | ||
signerAddress, | ||
lifiDiamondAddress, | ||
amount, | ||
publicClient | ||
) | ||
|
||
let estimatedFees | ||
try { | ||
estimatedFees = ( | ||
await airliftContract.simulate.quoteSend([ | ||
SRC_TOKEN_ADDRESS, | ||
amount, | ||
zeroPadAddressToBytes32(signerAddress), | ||
BigInt(destinationChainId), | ||
signerAddress, | ||
parseEther('1'), | ||
]) | ||
).result as any | ||
|
||
if (!estimatedFees) { | ||
throw new Error('Invalid fee estimation from quoteSend.') | ||
} | ||
} catch (error) { | ||
console.error('Fee estimation failed:', error) | ||
process.exit(1) | ||
} | ||
|
||
const structuredFees = { | ||
gmpFee: { | ||
nativeFee: estimatedFees.gmpFee.nativeFee as bigint, | ||
tokenFee: estimatedFees.gmpFee.tokenFee as bigint, | ||
}, | ||
airliftFee: { | ||
nativeFee: estimatedFees.airliftFeeInfo.airliftFee.nativeFee as bigint, | ||
tokenFee: estimatedFees.airliftFeeInfo.airliftFee.tokenFee as bigint, | ||
}, | ||
} | ||
const nativeFee = | ||
structuredFees.gmpFee.nativeFee + structuredFees.airliftFee.nativeFee | ||
|
||
console.info(`Estimated native fee: ${nativeFee}`) | ||
|
||
// === Prepare bridge data === | ||
const bridgeData: ILiFi.BridgeDataStruct = { | ||
transactionId: `0x${randomBytes(32).toString('hex')}`, | ||
bridge: 'glacis', | ||
integrator: 'ACME Devs', | ||
referrer: zeroAddress, | ||
sendingAssetId: SRC_TOKEN_ADDRESS, | ||
receiver: signerAddress, | ||
destinationChainId, | ||
minAmount: amount, | ||
hasSourceSwaps: false, | ||
hasDestinationCall: false, | ||
} | ||
|
||
const glacisData: GlacisFacet.GlacisDataStruct = { | ||
refundAddress: signerAddress, | ||
nativeFee, | ||
} | ||
|
||
// === Start bridging === | ||
await executeTransaction( | ||
() => | ||
lifiDiamondContract.write.startBridgeTokensViaGlacis( | ||
[bridgeData, glacisData], | ||
{ value: nativeFee } | ||
), | ||
'Starting bridge tokens via Glacis', | ||
publicClient, | ||
true | ||
) | ||
} | ||
|
||
main() | ||
.then(() => process.exit(0)) | ||
.catch((error) => { | ||
console.error(error) | ||
process.exit(1) | ||
}) |
Oops, something went wrong.