Skip to content

Commit

Permalink
Scaffold Relay Facet
Browse files Browse the repository at this point in the history
  • Loading branch information
ezynda3 committed Oct 29, 2024
1 parent 26a04dd commit 2e55975
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 1 deletion.
16 changes: 16 additions & 0 deletions config/relay.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"mainnet": {
"example": "0x0000000000000000000000000000000000000000",
"exampleAllowedTokens": [
"0x0000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000"
]
},
"arbitrum": {
"example": "0x0000000000000000000000000000000000000000",
"exampleAllowedTokens": [
"0x0000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000"
]
}
}
92 changes: 92 additions & 0 deletions docs/RelayFacet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Relay Facet

## How it works

The Relay Facet works by ...

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

## Public Methods

- `function startBridgeTokensViaRelay(BridgeData calldata _bridgeData, RelayData calldata _relayData)`
- Simply bridges tokens using relay
- `swapAndStartBridgeTokensViarelay(BridgeData memory _bridgeData, LibSwap.SwapData[] calldata _swapData, relayData memory _relayData)`
- Performs swap(s) before bridging tokens using relay

## relay Specific Parameters

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

```solidity
/// @param example Example parameter.
struct relayData {
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: 'relay', // 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=relay&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=relay&fromAddress={YOUR_WALLET_ADDRESS}'
```
33 changes: 33 additions & 0 deletions script/deploy/facets/DeployRelayFacet.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

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

contract DeployScript is DeployScriptBase {
using stdJson for string;

constructor() DeployScriptBase("RelayFacet") {}

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

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

function getConstructorArgs() internal override returns (bytes memory) {
// If you don't have a constructor or it doesn't take any arguments, you can remove this function
string memory path = string.concat(root, "/config/relay.json");
string memory json = vm.readFile(path);

address example = json.readAddress(
string.concat(".", network, ".example")
);

return abi.encode(example);
}
}
24 changes: 24 additions & 0 deletions script/deploy/facets/UpdateRelayFacet.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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 { RelayFacet } from "lifi/Facets/RelayFacet.sol";

contract DeployScript is UpdateScriptBase {
using stdJson for string;

struct Config {
uint256 a;
bool b;
address c;
}

function run()
public
returns (address[] memory facets, bytes memory cutData)
{
return update("RelayFacet");
}
}
123 changes: 123 additions & 0 deletions src/Facets/RelayFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import { ILiFi } from "../Interfaces/ILiFi.sol";
import { LibDiamond } from "../Libraries/LibDiamond.sol";
import { LibAsset } from "../Libraries/LibAsset.sol";
import { LibSwap } from "../Libraries/LibSwap.sol";
import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol";
import { SwapperV2 } from "../Helpers/SwapperV2.sol";
import { Validatable } from "../Helpers/Validatable.sol";

/// @title Relay Facet
/// @author LI.FI (https://li.fi)
/// @notice Provides functionality for bridging through Relay Protocol
/// @custom:version 1.0.0
contract RelayFacet is ILiFi, ReentrancyGuard, SwapperV2, Validatable {
/// Storage ///

// Receiver for native transfers
address public immutable relayReceiver;
address public immutable relaySolver;

/// Types ///

/// @dev Optional bridge specific struct
/// @param exampleParam Example parameter
struct RelayData {
bytes32 requestId;
address receivingAssetId;
bytes signature;
}

/// Modifiers ///
modifier isValidQuote(
ILiFi.BridgeData calldata _bridgeData,
RelayData calldata _relayData
) {
// TODO: Verify the following
// requestId bytes32
// originChainId uint256
// sender bytes32(address)
// sendingAssetId bytes32(address)
// dstChainId uint256
// receiver bytes32(address)
// receivingAssetId bytes32(address)
_;
}

/// Constructor ///

constructor(address _relayReceiver, address _relaySolver) {
relayReceiver = _relayReceiver;
relaySolver = _relaySolver;
}

/// External Methods ///

/// @notice Bridges tokens via Relay
/// @param _bridgeData The core information needed for bridging
/// @param _relayData Data specific to Relay
function startBridgeTokensViaRelay(
ILiFi.BridgeData memory _bridgeData,
RelayData calldata _relayData
)
external
payable
nonReentrant
refundExcessNative(payable(msg.sender))
validateBridgeData(_bridgeData)
doesNotContainSourceSwaps(_bridgeData)
doesNotContainDestinationCalls(_bridgeData)
{
LibAsset.depositAsset(
_bridgeData.sendingAssetId,
_bridgeData.minAmount
);
_startBridge(_bridgeData, _relayData);
}

/// @notice Performs a swap before bridging via Relay
/// @param _bridgeData The core information needed for bridging
/// @param _swapData An array of swap related data for performing swaps before bridging
/// @param _relayData Data specific to Relay
function swapAndStartBridgeTokensViaRelay(
ILiFi.BridgeData memory _bridgeData,
LibSwap.SwapData[] calldata _swapData,
RelayData calldata _relayData
)
external
payable
nonReentrant
refundExcessNative(payable(msg.sender))
containsSourceSwaps(_bridgeData)
doesNotContainDestinationCalls(_bridgeData)
validateBridgeData(_bridgeData)
{
_bridgeData.minAmount = _depositAndSwap(
_bridgeData.transactionId,
_bridgeData.minAmount,
_swapData,
payable(msg.sender)
);
_startBridge(_bridgeData, _relayData);
}

/// Internal Methods ///

/// @dev Contains the business logic for the bridge via Relay
/// @param _bridgeData The core information needed for bridging
/// @param _relayData Data specific to Relay
function _startBridge(
ILiFi.BridgeData memory _bridgeData,
RelayData calldata _relayData
) internal {
// check if sendingAsset is native or ERC20
if (LibAsset.isNativeAsset(_bridgeData.sendingAssetId)) {
// Native
} else {
// ERC20
}
emit LiFiTransferStarted(_bridgeData);
}
}
2 changes: 1 addition & 1 deletion templates/facetDeployScript.template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract DeployScript is DeployScriptBase {
string memory path = string.concat(root, "/config/{{camelCase name}}.json");
string memory json = vm.readFile(path);

address acrossSpokePool = json.readAddress(
address example = json.readAddress(
string.concat(".", network, ".example")
);

Expand Down
Loading

0 comments on commit 2e55975

Please sign in to comment.