Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 2 additions & 7 deletions deploy/029_deploy_executor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ethers, network } from 'hardhat'
import { DeployFunction } from 'hardhat-deploy/types'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import config from '../config/axelar'
import sgConfig from '../config/stargate'
import { Executor, ERC20Proxy, PeripheryRegistryFacet } from '../typechain'

Expand All @@ -11,11 +10,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {

const { deployer } = await getNamedAccounts()

let gateway = ethers.constants.AddressZero
let sgRouter = ethers.constants.AddressZero
if (config[network.name]) {
gateway = config[network.name].gateway
}
if (sgConfig[network.name]) {
sgRouter = sgConfig[network.name].stargateRouter
}
Expand Down Expand Up @@ -49,7 +44,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
await deploy('Executor', {
from: deployer,
log: true,
args: [deployer, gateway, sgRouter, erc20Proxy.address],
args: [deployer, sgRouter, erc20Proxy.address],
deterministicDeployment: true,
})

Expand All @@ -68,7 +63,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
try {
await hre.run('verify:verify', {
address: executor.address,
constructorArguments: [deployer, gateway, sgRouter, erc20Proxy.address],
constructorArguments: [deployer, sgRouter, erc20Proxy.address],
})
} catch (e) {
console.log(`Failed to verify contract: ${e}`)
Expand Down
59 changes: 59 additions & 0 deletions deploy/032_deploy_axelar_executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ethers, network } from 'hardhat'
import { DeployFunction } from 'hardhat-deploy/types'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import config from '../config/axelar'
import { AxelarExecutor, PeripheryRegistryFacet } from '../typechain'

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre
const { deploy } = deployments

const { deployer } = await getNamedAccounts()

let gateway = ethers.constants.AddressZero
if (config[network.name]) {
gateway = config[network.name].gateway
}

const diamond = await ethers.getContract('LiFiDiamond')

const registryFacet = <PeripheryRegistryFacet>(
await ethers.getContractAt('PeripheryRegistryFacet', diamond.address)
)

await deploy('AxelarExecutor', {
from: deployer,
log: true,
args: [deployer, gateway],
deterministicDeployment: true,
})

const executor: AxelarExecutor = await ethers.getContract('AxelarExecutor')

const executorAddr = await registryFacet.getPeripheryContract(
'AxelarExecutor'
)

if (executorAddr !== executor.address) {
console.log('Updating periphery registry...')
await registryFacet.registerPeripheryContract(
'AxelarExecutor',
executor.address
)
console.log('Done!')
}

try {
await hre.run('verify:verify', {
address: executor.address,
constructorArguments: [deployer, gateway],
})
} catch (e) {
console.log(`Failed to verify contract: ${e}`)
}
}

export default func
func.id = 'deploy_axelar_executor'
func.tags = ['DeployAxelarExecutor']
func.dependencies = ['DeployPeripheryRegistryFacet']
19 changes: 19 additions & 0 deletions docs/AxelarExecutor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Executor

## Description

Periphery contract used for aribitrary cross-chain execution using Axelar

## How To Use

The contract is used to parse payloads sent cross-chain using the Axelar cross-chain execution platform.

The following external methods are available:

The contract has one utility method for updating the Axelar gateway

```solidity
/// @notice set the Axelar gateway
/// @param _gateway the Axelar gateway address
function setAxelarGateway(address _gateway)
```
6 changes: 1 addition & 5 deletions docs/Executor.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Periphery contract used for aribitrary cross-chain and same chain execution, swa
## How To Use

The contract has a number of methods that can be called depending on the context of the transaction
and which third-party integration is being used (Stargate, Axelar, etc).
and which third-party integration is being used.

The following methods are available:

Expand Down Expand Up @@ -69,10 +69,6 @@ function swapAndExecute(
The contract also has a number of utility methods that are self-explanatory

```solidity
/// @notice set the Axelar gateway
/// @param _gateway the Axelar gateway address
function setAxelarGateway(address _gateway)

/// @notice set Stargate Router
/// @param _router the Stargate router address
function setStargateRouter(address _router)
Expand Down
95 changes: 95 additions & 0 deletions src/Periphery/AxelarExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: Unlicensed
pragma solidity 0.8.13;

import { IAxelarExecutable } from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarExecutable.sol";
import { IAxelarGateway } from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol";
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol";
import { LibBytes } from "../Libraries/LibBytes.sol";
import { LibAsset } from "../Libraries/LibAsset.sol";

contract AxelarExecutor is IAxelarExecutable, Ownable, ReentrancyGuard {
using LibBytes for bytes;
using SafeERC20 for IERC20;

/// Errors ///
error UnAuthorized();
error ExecutionFailed();
error NotAContract();

/// Events ///
event AxelarGatewaySet(address indexed gateway);
event AxelarExecutionComplete(address indexed callTo, bytes4 selector);

/// Constructor ///
constructor(address _owner, address _gateway) IAxelarExecutable(_gateway) {
transferOwnership(_owner);
emit AxelarGatewaySet(_gateway);
}

/// External Methods ///

/// @notice set the Axelar gateway
/// @param _gateway the Axelar gateway address
function setAxelarGateway(address _gateway) external onlyOwner {
gateway = IAxelarGateway(_gateway);
emit AxelarGatewaySet(_gateway);
}

/// Internal Methods ///

/// @dev override of IAxelarExecutable _execute()
/// @notice handles the parsing and execution of the payload
/// @param payload the abi.encodePacked payload [callTo:callData]
function _execute(
string memory,
string memory,
bytes calldata payload
) internal override nonReentrant {
// The first 20 bytes of the payload are the callee address
address callTo = payload.toAddress(0);

if (callTo == address(gateway)) revert UnAuthorized();
if (!LibAsset.isContract(callTo)) revert NotAContract();

// The remaining bytes should be calldata
bytes memory callData = payload.slice(20, payload.length - 20);

(bool success, ) = callTo.call(callData);
if (!success) revert ExecutionFailed();
emit AxelarExecutionComplete(callTo, bytes4(callData));
}

/// @dev override of IAxelarExecutable _executeWithToken()
/// @notice handles the parsing and execution of the payload
/// @param payload the abi.encodePacked payload [callTo:callData]
/// @param tokenSymbol symbol of the token being bridged
/// @param amount of tokens being bridged
function _executeWithToken(
string memory,
string memory,
bytes calldata payload,
string memory tokenSymbol,
uint256 amount
) internal override nonReentrant {
// The first 20 bytes of the payload are the callee address
address callTo = payload.toAddress(0);

if (callTo == address(gateway)) revert UnAuthorized();
if (!LibAsset.isContract(callTo)) revert NotAContract();

// The remaining bytes should be calldata
bytes memory callData = payload.slice(20, payload.length - 20);

// get ERC-20 address from gateway
address tokenAddress = gateway.tokenAddresses(tokenSymbol);

// transfer received tokens to the recipient
IERC20(tokenAddress).safeApprove(callTo, 0);
IERC20(tokenAddress).safeApprove(callTo, amount);

(bool success, ) = callTo.call(callData);
if (!success) revert ExecutionFailed();
}
}
74 changes: 2 additions & 72 deletions src/Periphery/Executor.sol
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import { IAxelarExecutable } from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarExecutable.sol";
import { IAxelarGasService } from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGasService.sol";
import { IAxelarGateway } from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol";
import { IERC20 } from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IERC20.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ReentrancyGuard } from "../Helpers/ReentrancyGuard.sol";
import { LibSwap } from "../Libraries/LibSwap.sol";
import { LibAsset } from "../Libraries/LibAsset.sol";
import { ILiFi } from "../Interfaces/ILiFi.sol";
import { IERC20Proxy } from "../Interfaces/IERC20Proxy.sol";
import "../Libraries/LibBytes.sol";

/// @title Executor
/// @author LI.FI (https://li.fi)
/// @notice Arbitrary execution contract used for cross-chain swaps and message passing
contract Executor is IAxelarExecutable, Ownable, ReentrancyGuard, ILiFi {
using LibBytes for bytes;

contract Executor is Ownable, ReentrancyGuard, ILiFi {
/// Storage ///
address public sgRouter;
IERC20Proxy public erc20Proxy;
Expand All @@ -30,10 +24,8 @@ contract Executor is IAxelarExecutable, Ownable, ReentrancyGuard, ILiFi {
error UnAuthorized();

/// Events ///
event AxelarGatewaySet(address indexed gateway);
event StargateRouterSet(address indexed router);
event ERC20ProxySet(address indexed proxy);
event AxelarExecutionComplete(address indexed callTo, bytes4 selector);

/// Modifiers ///

Expand All @@ -59,27 +51,18 @@ contract Executor is IAxelarExecutable, Ownable, ReentrancyGuard, ILiFi {
/// Constructor
constructor(
address _owner,
address _gateway,
address _sgRouter,
address _erc20Proxy
) IAxelarExecutable(_gateway) {
) {
transferOwnership(_owner);
sgRouter = _sgRouter;
erc20Proxy = IERC20Proxy(_erc20Proxy);
emit AxelarGatewaySet(_gateway);
emit StargateRouterSet(_sgRouter);
emit ERC20ProxySet(_erc20Proxy);
}

/// External Methods ///

/// @notice set the Axelar gateway
/// @param _gateway the Axelar gateway address
function setAxelarGateway(address _gateway) external onlyOwner {
gateway = IAxelarGateway(_gateway);
emit AxelarGatewaySet(_gateway);
}

/// @notice set Stargate Router
/// @param _router the Stargate router address
function setStargateRouter(address _router) external onlyOwner {
Expand Down Expand Up @@ -264,59 +247,6 @@ contract Executor is IAxelarExecutable, Ownable, ReentrancyGuard, ILiFi {
emit LiFiTransferCompleted(_lifiData.transactionId, transferredAssetId, receiver, amount, block.timestamp);
}

/// Internal Methods ///

/// @dev override of IAxelarExecutable _execute()
/// @notice handles the parsing and execution of the payload
/// @param payload the abi.encodePacked payload [callTo:callData]
function _execute(
string memory,
string memory,
bytes calldata payload
) internal override nonReentrant {
// The first 20 bytes of the payload are the callee address
address callTo = payload.toAddress(0);

if (callTo == address(erc20Proxy)) revert UnAuthorized();

// The remaining bytes should be calldata
bytes memory callData = payload.slice(20, payload.length - 20);

(bool success, ) = callTo.call(callData);
if (!success) revert ExecutionFailed();
emit AxelarExecutionComplete(callTo, bytes4(callData));
}

/// @dev override of IAxelarExecutable _executeWithToken()
/// @notice handles the parsing and execution of the payload
/// @param payload the abi.encodePacked payload [callTo:callData]
/// @param tokenSymbol symbol of the token being bridged
/// @param amount of tokens being bridged
function _executeWithToken(
string memory,
string memory,
bytes calldata payload,
string memory tokenSymbol,
uint256 amount
) internal override nonReentrant {
// The first 20 bytes of the payload are the callee address
address callTo = payload.toAddress(0);

if (callTo == address(erc20Proxy)) revert UnAuthorized();

// The remaining bytes should be calldata
bytes memory callData = payload.slice(20, payload.length - 20);

// get ERC-20 address from gateway
address tokenAddress = gateway.tokenAddresses(tokenSymbol);

// transfer received tokens to the recipient
IERC20(tokenAddress).approve(callTo, amount);

(bool success, ) = callTo.call(callData);
if (!success) revert ExecutionFailed();
}

/// Private Methods ///

/// @dev Executes swaps one after the other
Expand Down
Loading