Skip to content

Commit

Permalink
basic implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ezynda3 committed Feb 10, 2025
1 parent 2cc7956 commit 9870fed
Show file tree
Hide file tree
Showing 8 changed files with 617 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config/chainflip.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mainnet": {
"chainflipVault": "0xF5e10380213880111522dd0efD3dbb45b9f62Bcc"
},
"arbitrum": {
"chainflipVault": "0x79001a5e762f3bEFC8e5871b42F6734e00498920"
}
}
92 changes: 92 additions & 0 deletions docs/ChainflipFacet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Chainflip Facet

## How it works

The Chainflip Facet works by ...

```mermaid
graph LR;
D{LiFiDiamond}-- DELEGATECALL -->ChainflipFacet;
ChainflipFacet -- CALL --> C(Chainflip)
```

## Public Methods

- `function startBridgeTokensViaChainflip(BridgeData calldata _bridgeData, ChainflipData calldata _chainflipData)`
- Simply bridges tokens using chainflip
- `swapAndStartBridgeTokensViaChainflip(BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, chainflipData memory _chainflipData)`
- Performs swap(s) before bridging tokens using chainflip

## chainflip Specific Parameters

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

```solidity
/// @param example Example parameter.
struct chainflipData {
string example;
}
```

## 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: 'chainflip', // 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=chainflip&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=chainflip&fromAddress={YOUR_WALLET_ADDRESS}'
```
132 changes: 132 additions & 0 deletions script/demoScripts/demoChainflip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { getContract, parseUnits, Narrow, zeroAddress } from 'viem'
import { randomBytes } from 'crypto'
import dotenv from 'dotenv'
import erc20Artifact from '../../out/ERC20/ERC20.sol/ERC20.json'
import chainflipFacetArtifact from '../../out/ChainflipFacet.sol/ChainflipFacet.json'
import { ChainflipFacet, ILiFi } from '../../typechain'
import { SupportedChain } from './utils/demoScriptChainConfig'
import {
ensureBalance,
ensureAllowance,
executeTransaction,
setupEnvironment,
} from './utils/demoScriptHelpers'

dotenv.config()

// #region ABIs

const ERC20_ABI = erc20Artifact.abi as Narrow<typeof erc20Artifact.abi>
const CHAINFLIP_FACET_ABI = chainflipFacetArtifact.abi as Narrow<
typeof chainflipFacetArtifact.abi
>

// #endregion

dotenv.config()

async function main() {
// === Set up environment ===
const srcChain: SupportedChain = 'mainnet' // Set source chain
const destinationChainId = 1 // Set destination chain id

const {
client,
publicClient,
walletAccount,
lifiDiamondAddress,
lifiDiamondContract,
} = await setupEnvironment(srcChain, CHAINFLIP_FACET_ABI)
const signerAddress = walletAccount.address

// === Contract addresses ===
const SRC_TOKEN_ADDRESS = '' as `0x${string}` // Set the source token address here.

// If you need to retrieve a specific address from your config file
// based on the chain and element name, use this helper function.
//
// First, ensure you import the relevant config file:
// import config from '../../config/chainflip.json'
//
// Then, retrieve the address:
// const EXAMPLE_ADDRESS = getConfigElement(config, srcChain, 'example');
//

// === Instantiate contracts ===
const srcTokenContract = getContract({
address: SRC_TOKEN_ADDRESS,
abi: ERC20_ABI,
client,
})

// If you need to interact with a contract, use the following helper.
// Provide the contract address, ABI, and a client instance to initialize
// the contract for both read and write operations.
//
// const exampleContract = getContract({
// address: EXAMPLE_ADDRESS,
// abi: EXAMPLE_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('10', srcTokenDecimals) // 10 * 1e{source token decimals}

console.info(
`\nBridge ${amount} ${srcTokenName} (${srcTokenSymbol}) from ${srcChain} --> {DESTINATION CHAIN NAME}`
)
console.info(`Connected wallet address: ${signerAddress}`)

await ensureBalance(srcTokenContract, signerAddress, amount)

await ensureAllowance(
srcTokenContract,
signerAddress,
lifiDiamondAddress,
amount,
publicClient
)

// === In this part put necessary logic usually it's fetching quotes, estimating fees, signing messages etc. ===

// === Prepare bridge data ===
const bridgeData: ILiFi.BridgeDataStruct = {
// Edit fields as needed
transactionId: `0x${randomBytes(32).toString('hex')}`,
bridge: 'chainflip',
integrator: 'ACME Devs',
referrer: zeroAddress,
sendingAssetId: SRC_TOKEN_ADDRESS,
receiver: signerAddress,
destinationChainId,
minAmount: amount,
hasSourceSwaps: false,
hasDestinationCall: false,
}

const chainflipData: ChainflipFacet.ChainflipDataStruct = {
// Add your specific fields for Chainflip here.
}

// === Start bridging ===
await executeTransaction(
() =>
lifiDiamondContract.write.startBridgeTokensViaChainflip(
[bridgeData, chainflipData]
// { value: fee } optional value
),
'Starting bridge tokens via Chainflip',
publicClient,
true
)
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
34 changes: 34 additions & 0 deletions script/deploy/facets/DeployChainflipFacet.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import { DeployScriptBase } from "./utils/DeployScriptBase.sol";
import { stdJson } from "forge-std/Script.sol";
import { ChainflipFacet } from "lifi/Facets/ChainflipFacet.sol";

contract DeployScript is DeployScriptBase {
using stdJson for string;

constructor() DeployScriptBase("ChainflipFacet") {}

function run()
public
returns (ChainflipFacet deployed, bytes memory constructorArgs)
{
constructorArgs = getConstructorArgs();

deployed = ChainflipFacet(deploy(type(ChainflipFacet).creationCode));
}

function getConstructorArgs() internal override returns (bytes memory) {
// get path of chainflip config file
string memory path = string.concat(root, "/config/chainflip.json");

// Read the Chainflip vault address from config
address chainflipVault = _getConfigContractAddress(
path,
string.concat(".", network, ".chainflipVault")
);

return abi.encode(chainflipVault);
}
}
28 changes: 28 additions & 0 deletions script/deploy/facets/UpdateChainflipFacet.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import { UpdateScriptBase } from "./utils/UpdateScriptBase.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { DiamondCutFacet, IDiamondCut } from "lifi/Facets/DiamondCutFacet.sol";
import { ChainflipFacet } from "lifi/Facets/ChainflipFacet.sol";

contract DeployScript is UpdateScriptBase {
using stdJson for string;

function run()
public
returns (address[] memory facets, bytes memory cutData)
{
return update("ChainflipFacet");
}

function getExcludes() internal pure override returns (bytes4[] memory) {
// No functions to exclude
return new bytes4[](0);
}

function getCallData() internal override returns (bytes memory) {
// No initialization needed
return new bytes(0);
}
}
Loading

0 comments on commit 9870fed

Please sign in to comment.