Skip to content

Commit

Permalink
Merge pull request #945 from lifinance/implement-glacis-LF-11761
Browse files Browse the repository at this point in the history
LF-11761 - Implement glacis [GlacisFacet v1.0.0,IGlacisAirlift v1.0.0]
  • Loading branch information
mirooon authored Mar 4, 2025
2 parents 95efca3 + 7f2ebf6 commit e4d597c
Show file tree
Hide file tree
Showing 16 changed files with 1,079 additions and 14 deletions.
13 changes: 13 additions & 0 deletions audit/auditLog.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,13 @@
"auditReportPath": "./audit/reports/2025.01.17_LiFiDexAggregator(v1.4.0).pdf",
"auditCommitHash": "n/a (one deployed contract instance was audited)"
},
"audit20250219": {
"auditCompletedOn": "19.02.2025",
"auditedBy": "Sujith Somraaj (individual security researcher)",
"auditorGitHandle": "sujithsomraaj",
"auditReportPath": "./audit/reports/2025.02.19_GlacisFacet(v1.0.0).pdf",
"auditCommitHash": "9e623bc2a218399172e734af0ba6dfa3f76963a5"
},
"audit20250220": {
"auditCompletedOn": "20.02.2025",
"auditedBy": "Sujith Somraaj (individual security researcher)",
Expand Down Expand Up @@ -206,12 +213,18 @@
"1.0.0": ["audit20241107"],
"1.0.1": ["audit20250110_1"]
},
"GlacisFacet": {
"1.0.0": ["audit20250219"]
},
"IAcrossSpokePool": {
"1.0.0": ["audit20250106"]
},
"IGasZip": {
"1.0.0": ["audit20241107"]
},
"IGlacisAirlift": {
"1.0.0": ["audit20250219"]
},
"LibAsset": {
"1.0.1": ["audit20241202"],
"1.0.2": ["audit20250110_1"]
Expand Down
Binary file added audit/reports/2025.02.19_GlacisFacet(v1.0.0).pdf
Binary file not shown.
12 changes: 12 additions & 0 deletions config/glacis.json
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"
}
}
16 changes: 16 additions & 0 deletions deployments/_deployments_log_file.json
Original file line number Diff line number Diff line change
Expand Up @@ -29764,5 +29764,21 @@
]
}
}
},
"GlacisFacet": {
"arbitrum": {
"staging": {
"1.0.0": [
{
"ADDRESS": "0xF82830B952Bc60b93206FA22f1cD4770cedb2840",
"OPTIMIZER_RUNS": "1000000",
"TIMESTAMP": "2025-02-26 15:22:09",
"CONSTRUCTOR_ARGS": "0x000000000000000000000000d9e7f6f7dc7517678127d84dbf0f0b4477de14e0",
"SALT": "",
"VERIFIED": "false"
}
]
}
}
}
}
15 changes: 11 additions & 4 deletions deployments/arbitrum.diamond.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,20 @@
"Version": "1.0.0"
},
"0xE15C7585636e62b88bA47A40621287086E0c2E33": {
"Name": "",
"Version": ""
"Name": "DeBridgeDlnFacet",
"Version": "1.0.0"
},
"0x08BfAc22A3B41637edB8A7920754fDb30B18f740": {
"Name": "AcrossFacetV3",
"Version": "1.1.0"
},
"0xF82830B952Bc60b93206FA22f1cD4770cedb2840": {
"Name": "GlacisFacet",
"Version": "1.0.0"
},
"0xDd661337B48BEA5194F6d26F2C59fF0855E15289": {
"Name": "",
"Version": ""
}
},
"Periphery": {
Expand All @@ -153,9 +161,8 @@
"GasZipPeriphery": "",
"LiFiDEXAggregator": "",
"LiFuelFeeCollector": "0x94EA56D8049e93E0308B9c7d1418Baf6A7C68280",
"Permit2Proxy": "0x6FC01BC9Ff6Cdab694Ec8Ca41B21a2F04C8c37E5",
"Permit2Proxy": "0xb33Fe241BEd9bf5F694101D7498F63a0d060F999",
"ReceiverAcrossV3": "0xe4F3DEF14D61e47c696374453CD64d438FD277F8",
"Receiver": "0x36E9B2E8A627474683eF3b1E9Df26D2bF04396f3",
"ReceiverStargateV2": "",
"RelayerCelerIM": "0xa1Ed8783AC96385482092b82eb952153998e9b70",
"TokenWrapper": "0xF63b27AE2Dc887b88f82E2Cc597d07fBB2E78E70"
Expand Down
4 changes: 2 additions & 2 deletions deployments/arbitrum.staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"DeBridgeDlnFacet": "0xE15C7585636e62b88bA47A40621287086E0c2E33",
"MayanFacet": "0xd596C903d78870786c5DB0E448ce7F87A65A0daD",
"StandardizedCallFacet": "0xA7ffe57ee70Ac4998e9E9fC6f17341173E081A8f",
"MayanFacet": "0xd596C903d78870786c5DB0E448ce7F87A65A0daD",
"GenericSwapFacetV3": "0xFf6Fa203573Baaaa4AE375EB7ac2819d539e16FF",
"CalldataVerificationFacet": "0x90B5b319cA20D9E466cB5b843952363C34d1b54E",
"AcrossFacetPacked": "0x7A3770a9504924d99D38BBba4F0116B756393Eb3",
Expand All @@ -51,5 +50,6 @@
"AcrossFacetV3": "0x08BfAc22A3B41637edB8A7920754fDb30B18f740",
"ReceiverAcrossV3": "0xe4F3DEF14D61e47c696374453CD64d438FD277F8",
"AcrossFacetPackedV3": "0x21767081Ff52CE5563A29f27149D01C7127775A2",
"RelayFacet": "0x3cf7dE0e31e13C93c8Aada774ADF1C7eD58157f5"
"RelayFacet": "0x3cf7dE0e31e13C93c8Aada774ADF1C7eD58157f5",
"GlacisFacet": "0xF82830B952Bc60b93206FA22f1cD4770cedb2840"
}
94 changes: 94 additions & 0 deletions docs/GlacisFacet.md
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}'
```
159 changes: 159 additions & 0 deletions script/demoScripts/demoGlacis.ts
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)
})
Loading

0 comments on commit e4d597c

Please sign in to comment.