ETH Price: $2,432.76 (+1.15%)

Transaction Decoder

Block:
17011440 at Apr-09-2023 02:52:11 PM +UTC
Transaction Fee:
0.000846369966362886 ETH $2.06
Gas Used:
36,693 Gas / 23.066251502 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x2d5d7d31...09a082712
(Axelar: Gas Service)
1.554616028118103616 Eth1.553136663210563638 Eth0.001479364907539978
0x345662AB...74cCb6A90
(Axelar: Relayer 4)
1.307675573290822069 Eth
Nonce: 2987
1.306829203324459183 Eth
Nonce: 2988
0.000846369966362886
(Fee Recipient: 0xA8C...f64)
20.824933141160691671 Eth20.824988180660691671 Eth0.0000550395
0xd1a1436d...620B97368 0.008729561307764479 Eth0.010208926215304457 Eth0.001479364907539978

Execution Trace

AxelarGasServiceProxy.82ad6f35( )
  • AxelarGasService.refund( receiver=0xd1a1436dcC327049Ec73BC24956429c620B97368, token=0x0000000000000000000000000000000000000000, amount=1479364907539978 )
    • ETH 0.001479364907539978 0xd1a1436dcc327049ec73bc24956429c620b97368.CALL( )
      File 1 of 2: AxelarGasServiceProxy
      // Sources flattened with hardhat v2.9.9 https://hardhat.org
      
      // File contracts/interfaces/IUpgradable.sol
      
      // SPDX-License-Identifier: MIT
      
      pragma solidity 0.8.9;
      
      // General interface for upgradable contracts
      interface IUpgradable {
          error NotOwner();
          error InvalidOwner();
          error InvalidCodeHash();
          error InvalidImplementation();
          error SetupFailed();
          error NotProxy();
      
          event Upgraded(address indexed newImplementation);
          event OwnershipTransferred(address indexed newOwner);
      
          // Get current owner
          function owner() external view returns (address);
      
          function contractId() external pure returns (bytes32);
      
          function implementation() external view returns (address);
      
          function upgrade(
              address newImplementation,
              bytes32 newImplementationCodeHash,
              bytes calldata params
          ) external;
      
          function setup(bytes calldata data) external;
      }
      
      
      // File contracts/util/Proxy.sol
      
      contract Proxy {
          error InvalidImplementation();
          error SetupFailed();
          error EtherNotAccepted();
          error NotOwner();
          error AlreadyInitialized();
      
          // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
          bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          // keccak256('owner')
          bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
      
          constructor() {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(_OWNER_SLOT, caller())
              }
          }
      
          function init(
              address implementationAddress,
              address newOwner,
              bytes memory params
          ) external {
              address owner;
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  owner := sload(_OWNER_SLOT)
              }
              if (msg.sender != owner) revert NotOwner();
              if (implementation() != address(0)) revert AlreadyInitialized();
              if (IUpgradable(implementationAddress).contractId() != contractId()) revert InvalidImplementation();
      
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(_IMPLEMENTATION_SLOT, implementationAddress)
                  sstore(_OWNER_SLOT, newOwner)
              }
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, ) = implementationAddress.delegatecall(
                  // keccak('setup(bytes)') selector
                  abi.encodeWithSelector(0x9ded06df, params)
              );
              if (!success) revert SetupFailed();
          }
      
          // solhint-disable-next-line no-empty-blocks
          function contractId() internal pure virtual returns (bytes32) {}
      
          function implementation() public view returns (address implementation_) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  implementation_ := sload(_IMPLEMENTATION_SLOT)
              }
          }
      
          // solhint-disable-next-line no-empty-blocks
          function setup(bytes calldata data) public {}
      
          // solhint-disable-next-line no-complex-fallback
          fallback() external payable {
              address implementaion_ = implementation();
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  calldatacopy(0, 0, calldatasize())
      
                  let result := delegatecall(gas(), implementaion_, 0, calldatasize(), 0, 0)
                  returndatacopy(0, 0, returndatasize())
      
                  switch result
                  case 0 {
                      revert(0, returndatasize())
                  }
                  default {
                      return(0, returndatasize())
                  }
              }
          }
      
          receive() external payable virtual {
              revert EtherNotAccepted();
          }
      }
      
      
      // File contracts/gas-service/AxelarGasServiceProxy.sol
      
      contract AxelarGasServiceProxy is Proxy {
          function contractId() internal pure override returns (bytes32) {
              return keccak256('axelar-gas-service');
          }
      }

      File 2 of 2: AxelarGasService
      // Sources flattened with hardhat v2.9.9 https://hardhat.org
      
      // File contracts/interfaces/IUpgradable.sol
      
      // SPDX-License-Identifier: MIT
      
      pragma solidity 0.8.9;
      
      // General interface for upgradable contracts
      interface IUpgradable {
          error NotOwner();
          error InvalidOwner();
          error InvalidCodeHash();
          error InvalidImplementation();
          error SetupFailed();
          error NotProxy();
      
          event Upgraded(address indexed newImplementation);
          event OwnershipTransferred(address indexed newOwner);
      
          // Get current owner
          function owner() external view returns (address);
      
          function contractId() external pure returns (bytes32);
      
          function implementation() external view returns (address);
      
          function upgrade(
              address newImplementation,
              bytes32 newImplementationCodeHash,
              bytes calldata params
          ) external;
      
          function setup(bytes calldata data) external;
      }
      
      
      // File contracts/interfaces/IAxelarGasService.sol
      
      // This should be owned by the microservice that is paying for gas.
      interface IAxelarGasService is IUpgradable {
          error NothingReceived();
          error TransferFailed();
          error InvalidAddress();
          error NotCollector();
          error InvalidAmounts();
      
          event GasPaidForContractCall(
              address indexed sourceAddress,
              string destinationChain,
              string destinationAddress,
              bytes32 indexed payloadHash,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          );
      
          event GasPaidForContractCallWithToken(
              address indexed sourceAddress,
              string destinationChain,
              string destinationAddress,
              bytes32 indexed payloadHash,
              string symbol,
              uint256 amount,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          );
      
          event NativeGasPaidForContractCall(
              address indexed sourceAddress,
              string destinationChain,
              string destinationAddress,
              bytes32 indexed payloadHash,
              uint256 gasFeeAmount,
              address refundAddress
          );
      
          event NativeGasPaidForContractCallWithToken(
              address indexed sourceAddress,
              string destinationChain,
              string destinationAddress,
              bytes32 indexed payloadHash,
              string symbol,
              uint256 amount,
              uint256 gasFeeAmount,
              address refundAddress
          );
      
          event GasAdded(bytes32 indexed txHash, uint256 indexed logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress);
      
          event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress);
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payGasForContractCall(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          ) external;
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payGasForContractCallWithToken(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              string calldata symbol,
              uint256 amount,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          ) external;
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payNativeGasForContractCall(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              address refundAddress
          ) external payable;
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payNativeGasForContractCallWithToken(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              string calldata symbol,
              uint256 amount,
              address refundAddress
          ) external payable;
      
          function addGas(
              bytes32 txHash,
              uint256 txIndex,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          ) external;
      
          function addNativeGas(
              bytes32 txHash,
              uint256 logIndex,
              address refundAddress
          ) external payable;
      
          function collectFees(
              address payable receiver,
              address[] calldata tokens,
              uint256[] calldata amounts
          ) external;
      
          function refund(
              address payable receiver,
              address token,
              uint256 amount
          ) external;
      
          function gasCollector() external returns (address);
      }
      
      
      // File contracts/interfaces/IERC20.sol
      
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          error InvalidAccount();
      
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
      
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
      
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
      
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
      
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address sender,
              address recipient,
              uint256 amount
          ) external returns (bool);
      
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
      
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      
      
      // File contracts/util/Upgradable.sol
      
      abstract contract Upgradable is IUpgradable {
          // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
          bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
          // keccak256('owner')
          bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
      
          modifier onlyOwner() {
              if (owner() != msg.sender) revert NotOwner();
              _;
          }
      
          function owner() public view returns (address owner_) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  owner_ := sload(_OWNER_SLOT)
              }
          }
      
          function transferOwnership(address newOwner) external virtual onlyOwner {
              if (newOwner == address(0)) revert InvalidOwner();
      
              emit OwnershipTransferred(newOwner);
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(_OWNER_SLOT, newOwner)
              }
          }
      
          function implementation() public view returns (address implementation_) {
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  implementation_ := sload(_IMPLEMENTATION_SLOT)
              }
          }
      
          function upgrade(
              address newImplementation,
              bytes32 newImplementationCodeHash,
              bytes calldata params
          ) external override onlyOwner {
              if (IUpgradable(newImplementation).contractId() != IUpgradable(this).contractId()) revert InvalidImplementation();
              if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();
      
              if (params.length > 0) {
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(this.setup.selector, params));
      
                  if (!success) revert SetupFailed();
              }
      
              emit Upgraded(newImplementation);
              // solhint-disable-next-line no-inline-assembly
              assembly {
                  sstore(_IMPLEMENTATION_SLOT, newImplementation)
              }
          }
      
          function setup(bytes calldata data) external override {
              // Prevent setup from being called on the implementation
              if (implementation() == address(0)) revert NotProxy();
      
              _setup(data);
          }
      
          // solhint-disable-next-line no-empty-blocks
          function _setup(bytes calldata data) internal virtual {}
      }
      
      
      // File contracts/gas-service/AxelarGasService.sol
      
      // This should be owned by the microservice that is paying for gas.
      contract AxelarGasService is Upgradable, IAxelarGasService {
          address public immutable gasCollector;
      
          constructor(address gasCollector_) {
              gasCollector = gasCollector_;
          }
      
          modifier onlyCollector() {
              if (msg.sender != gasCollector) revert NotCollector();
      
              _;
          }
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payGasForContractCall(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          ) external override {
              _safeTransferFrom(gasToken, msg.sender, gasFeeAmount);
      
              emit GasPaidForContractCall(
                  sender,
                  destinationChain,
                  destinationAddress,
                  keccak256(payload),
                  gasToken,
                  gasFeeAmount,
                  refundAddress
              );
          }
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payGasForContractCallWithToken(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              string memory symbol,
              uint256 amount,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          ) external override {
              _safeTransferFrom(gasToken, msg.sender, gasFeeAmount);
      
              emit GasPaidForContractCallWithToken(
                  sender,
                  destinationChain,
                  destinationAddress,
                  keccak256(payload),
                  symbol,
                  amount,
                  gasToken,
                  gasFeeAmount,
                  refundAddress
              );
          }
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payNativeGasForContractCall(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              address refundAddress
          ) external payable override {
              if (msg.value == 0) revert NothingReceived();
      
              emit NativeGasPaidForContractCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
          }
      
          // This is called on the source chain before calling the gateway to execute a remote contract.
          function payNativeGasForContractCallWithToken(
              address sender,
              string calldata destinationChain,
              string calldata destinationAddress,
              bytes calldata payload,
              string calldata symbol,
              uint256 amount,
              address refundAddress
          ) external payable override {
              if (msg.value == 0) revert NothingReceived();
      
              emit NativeGasPaidForContractCallWithToken(
                  sender,
                  destinationChain,
                  destinationAddress,
                  keccak256(payload),
                  symbol,
                  amount,
                  msg.value,
                  refundAddress
              );
          }
      
          function addGas(
              bytes32 txHash,
              uint256 logIndex,
              address gasToken,
              uint256 gasFeeAmount,
              address refundAddress
          ) external override {
              _safeTransferFrom(gasToken, msg.sender, gasFeeAmount);
      
              emit GasAdded(txHash, logIndex, gasToken, gasFeeAmount, refundAddress);
          }
      
          function addNativeGas(
              bytes32 txHash,
              uint256 logIndex,
              address refundAddress
          ) external payable override {
              if (msg.value == 0) revert NothingReceived();
      
              emit NativeGasAdded(txHash, logIndex, msg.value, refundAddress);
          }
      
          function collectFees(
              address payable receiver,
              address[] calldata tokens,
              uint256[] calldata amounts
          ) external onlyCollector {
              if (receiver == address(0)) revert InvalidAddress();
      
              uint256 tokensLength = tokens.length;
              if (tokensLength != amounts.length) revert InvalidAmounts();
      
              for (uint256 i; i < tokensLength; i++) {
                  address token = tokens[i];
                  uint256 amount = amounts[i];
                  if (amount == 0) revert InvalidAmounts();
      
                  if (token == address(0)) {
                      if (amount <= address(this).balance) receiver.transfer(amount);
                  } else {
                      if (amount <= IERC20(token).balanceOf(address(this))) _safeTransfer(token, receiver, amount);
                  }
              }
          }
      
          function refund(
              address payable receiver,
              address token,
              uint256 amount
          ) external onlyCollector {
              if (receiver == address(0)) revert InvalidAddress();
      
              if (token == address(0)) {
                  receiver.transfer(amount);
              } else {
                  _safeTransfer(token, receiver, amount);
              }
          }
      
          function _safeTransfer(
              address tokenAddress,
              address receiver,
              uint256 amount
          ) internal {
              if (amount == 0) revert NothingReceived();
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returnData) = tokenAddress.call(abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
              bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
      
              if (!transferred || tokenAddress.code.length == 0) revert TransferFailed();
          }
      
          function _safeTransferFrom(
              address tokenAddress,
              address from,
              uint256 amount
          ) internal {
              if (amount == 0) revert NothingReceived();
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returnData) = tokenAddress.call(
                  abi.encodeWithSelector(IERC20.transferFrom.selector, from, address(this), amount)
              );
              bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
      
              if (!transferred || tokenAddress.code.length == 0) revert TransferFailed();
          }
      
          function contractId() external pure returns (bytes32) {
              return keccak256('axelar-gas-service');
          }
      }