Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(GasService): estimateGasFee and payGas #273

Merged
merged 22 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
89 changes: 86 additions & 3 deletions contracts/gas-service/AxelarGasService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,36 @@
pragma solidity ^0.8.0;

import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import { InterchainGasEstimation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/gas-estimation/InterchainGasEstimation.sol';
import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol';
import { SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { SafeNativeTransfer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeNativeTransfer.sol';
import { IAxelarGasService } from '../interfaces/IAxelarGasService.sol';
import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol';

/**
* @title AxelarGasService
* @notice This contract manages gas payments and refunds for cross-chain communication on the Axelar network.
* @dev The owner address of this contract should be the microservice that pays for gas.
* @dev Users pay gas for cross-chain calls, and the gasCollector can collect accumulated fees and/or refund users if needed.
*/
contract AxelarGasService is Upgradable, IAxelarGasService {
contract AxelarGasService is InterchainGasEstimation, Upgradable, IAxelarGasService {
using SafeTokenTransfer for IERC20;
using SafeTokenTransferFrom for IERC20;
using SafeNativeTransfer for address payable;

address public immutable gasCollector;

enum GasPaymentType {
NativeForContractCall,
NativeForContractCallWithToken,
TokenForContractCall,
TokenForContractCallWithToken,
NativeForExpressCall,
NativeForExpressCallWithToken,
TokenForExpressCall,
TokenForExpressCallWithToken
}

/**
* @notice Constructs the AxelarGasService contract.
* @param gasCollector_ The address of the gas collector
Expand All @@ -38,6 +50,58 @@ contract AxelarGasService is Upgradable, IAxelarGasService {
_;
}

/**
* @notice Pay for gas for any type of contract execution on a destination chain.
* @dev This function is called on the source chain before calling the gateway to execute a remote contract.
* @dev If estimateOnChain is true, the function will estimate the gas cost and revert if the payment is insufficient.
* @param sender The address making the payment
* @param destinationChain The target chain where the contract call will be made
* @param destinationAddress The target address on the destination chain
* @param payload Data payload for the contract call
* @param executionGasLimit The gas limit for the contract call
* @param estimateOnChain Flag to enable on-chain gas estimation
* @param refundAddress The address where refunds, if any, should be sent
* @param params Additional parameters for gas payment
*/
function payGas(
address sender,
string calldata destinationChain,
string calldata destinationAddress,
bytes calldata payload,
uint256 executionGasLimit,
bool estimateOnChain,
address refundAddress,
bytes calldata params
) external payable override {
GasPaymentType gasPaymentType = GasPaymentType.NativeForContractCall;

if (params.length >= 32) {
(gasPaymentType) = abi.decode(params, (GasPaymentType));
}

if (estimateOnChain) {
uint256 gasEstimate = estimateGasFee(
destinationChain,
destinationAddress,
payload,
executionGasLimit,
gasPaymentType >= GasPaymentType.NativeForExpressCall && gasPaymentType <= GasPaymentType.TokenForExpressCallWithToken
);

if (gasEstimate > msg.value) {
revert InsufficientGasPayment(gasEstimate, msg.value);
}
}

if (gasPaymentType == GasPaymentType.NativeForContractCall) {
emit NativeGasPaidForContractCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
return;
} else if (gasPaymentType == GasPaymentType.NativeForExpressCall) {
emit NativeGasPaidForExpressCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
return;
}
}

/**
* @notice Pay for gas using ERC20 tokens for a contract call on a destination chain.
* @dev This function is called on the source chain before calling the gateway to execute a remote contract.
Expand Down Expand Up @@ -348,6 +412,25 @@ contract AxelarGasService is Upgradable, IAxelarGasService {
emit NativeExpressGasAdded(txHash, logIndex, msg.value, refundAddress);
}

/**
* @notice Updates the gas price for a specific chain.
* @dev This function is called by the gas oracle to update the gas prices for a specific chains.
* @param chains Array of chain names
* @param gasUpdates Array of gas updates
*/
function updateGasInfo(string[] calldata chains, GasInfo[] calldata gasUpdates) external onlyCollector {
uint256 chainsLength = chains.length;

if (chainsLength != gasUpdates.length) revert InvalidGasUpdates();

for (uint256 i; i < chainsLength; i++) {
string calldata chain = chains[i];
GasInfo calldata gasUpdate = gasUpdates[i];

_setGasInfo(chain, gasUpdate);
}
}

/**
* @notice Allows the gasCollector to collect accumulated fees from the contract.
* @dev Use address(0) as the token address for native currency.
Expand Down
Loading
Loading