// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/// @title Serai Router (without functions overriden by selector collisions)
/// @author Luke Parker <lukeparker@serai.exchange>
/// @notice Intakes coins for the Serai network and handles relaying batches of transfers out
interface IRouterWithoutCollisions {
  /// @notice Emitted when the next key for Serai's Ethereum validators is set
  /// @param nonce The nonce consumed to update this key
  /// @param key The key updated to
  event NextSeraiKeySet(uint256 indexed nonce, bytes32 indexed key);

  /// @notice Emitted when the key for Serai's Ethereum validators is updated
  /// @param nonce The nonce consumed to update this key
  /// @param key The key updated to
  event SeraiKeyUpdated(uint256 indexed nonce, bytes32 indexed key);

  /// @notice Emitted when an InInstruction occurs
  /// @param from The address which called `inInstruction` and caused this event to be emitted
  /// @param coin The coin transferred in
  /// @param amount The amount of the coin transferred in
  /// @param instruction The Shorthand-encoded InInstruction for Serai to decode and handle
  event InInstruction(
    address indexed from, address indexed coin, uint256 amount, bytes instruction
  );

  /// @notice Emitted when a batch of `OutInstruction`s occurs
  /// @param nonce The nonce consumed to execute this batch of transactions
  /// @param messageHash The hash of the message signed for the executed batch
  /**
   * @param results The result of each `OutInstruction` executed. This is a bitmask with true
   *   representing success and false representing failure. The high bit (1 << 7) in the first byte
   *   is used for the first `OutInstruction`, before the next bit, and so on, before the next byte.
   *   An `OutInstruction` is considered as having succeeded if the call transferring ETH doesn't
   *   fail, the ERC20 transfer doesn't fail, and any executed code doesn't revert.
   */
  event Executed(uint256 indexed nonce, bytes32 indexed messageHash, bytes results);

  /// @notice Emitted when `escapeHatch` is invoked
  /// @param escapeTo The address to escape to
  event EscapeHatch(address indexed escapeTo);

  /// @notice Emitted when coins escape through the escape hatch
  /// @param coin The coin which escaped
  event Escaped(address indexed coin);

  /// @notice The key for Serai was invalid
  /// @dev This is incomplete and not always guaranteed to be thrown upon an invalid key
  error InvalidSeraiKey();
  /// @notice The contract has had its escape hatch invoked and won't accept further actions
  error EscapeHatchInvoked();
  /// @notice The signature was invalid
  error InvalidSignature();

  /// @notice The amount specified didn't match `msg.value`
  error AmountMismatchesMsgValue();
  /// @notice The call to an ERC20's `transferFrom` failed
  error TransferFromFailed();

  /// @notice `execute` was re-entered
  error ReenteredExecute();

  /// @notice An invalid address to escape to was specified.
  error InvalidEscapeAddress();
  /// @notice Escaping when escape hatch wasn't invoked.
  error EscapeHatchNotInvoked();

  /// @notice Transfer coins into Serai with an instruction
  /// @param coin The coin to transfer in (address(0) if Ether)
  /// @param amount The amount to transfer in (msg.value if Ether)
  /**
   * @param instruction The Shorthand-encoded InInstruction for Serai to associate with this
   *   transfer in
   */
  // Re-entrancy doesn't bork this function
  // slither-disable-next-line reentrancy-events
  function inInstruction(address coin, uint256 amount, bytes memory instruction) external payable;

  /// @notice Execute some arbitrary code within a secure sandbox
  /**
   * @dev This performs sandboxing by deploying this code with `CREATE`. This is an external
   *   function as we can't meter `CREATE`/internal functions. We work around this by calling this
   *   function with `CALL` (which we can meter). This does forward `msg.value` to the newly
   *  deployed contract.
   */
  /// @param code The code to execute
  function executeArbitraryCode(bytes memory code) external payable;

  /// @notice Escape coins after the escape hatch has been invoked
  /// @param coin The coin to escape
  function escape(address coin) external;

  /// @notice Fetch the next nonce to use by an action published to this contract
  /// return The next nonce to use by an action published to this contract
  function nextNonce() external view returns (uint256);

  /// @notice Fetch the next key for Serai's Ethereum validator set
  /// @return The next key for Serai's Ethereum validator set or bytes32(0) if none is currently set
  function nextSeraiKey() external view returns (bytes32);

  /// @notice Fetch the current key for Serai's Ethereum validator set
  /**
   * @return The current key for Serai's Ethereum validator set or bytes32(0) if none is currently
   * set
   */
  function seraiKey() external view returns (bytes32);

  /// @notice Fetch the address escaped to
  /// @return The address which was escaped to (address(0) if the escape hatch hasn't been invoked)
  function escapedTo() external view returns (address);
}

/// @title Serai Router
/// @author Luke Parker <lukeparker@serai.exchange>
/// @notice Intakes coins for the Serai network and handles relaying batches of transfers out
interface IRouter is IRouterWithoutCollisions {
  /// @title A signature
  /// @dev Thin wrapper around `c, s` to simplify the API
  struct Signature {
    bytes32 c;
    bytes32 s;
  }

  /// @title The type of destination
  /// @dev A destination is either an address or a blob of code to deploy and call
  enum DestinationType {
    Address,
    Code
  }

  /// @title A code destination
  /**
   * @dev If transferring an ERC20 to this destination, it will be transferred to the address the
   *   code will be deployed to. If transferring ETH, it will be transferred with the deployment of
   *   the code. `code` is deployed with CREATE (calling its constructor). The entire deployment
   *   (and associated sandboxing) must consume less than `gasLimit` units of gas or it will revert.
   */
  struct CodeDestination {
    uint32 gasLimit;
    bytes code;
  }

  /// @title An instruction to transfer coins out
  /// @dev Specifies a destination and amount but not the coin as that's assumed to be contextual
  struct OutInstruction {
    DestinationType destinationType;
    bytes destination;
    uint256 amount;
  }

  /// @notice Update the key representing Serai's Ethereum validators
  /**
   * @dev This does not validate the passed-in key as much as possible. This is accepted as the key
   *   won't actually be rotated to until it provides a signature confirming the update however
   *   (proving signatures can be made by the key in question and verified via our Schnorr
   *   contract).
   */
  // @param signature The signature by the current key authorizing this update
  /// @param signature The signature by the current key authorizing this update
  /// @param nextSeraiKeyVar The key to update to, once it confirms the update
  function updateSeraiKey(Signature calldata signature, bytes32 nextSeraiKeyVar) external;

  /// @notice Confirm the next key representing Serai's Ethereum validators, updating to it
  /// @param signature The signature by the next key confirming its validity
  function confirmNextSeraiKey(Signature calldata signature) external;

  /// @notice Execute a batch of `OutInstruction`s
  /**
   * @dev All `OutInstruction`s in a batch are only for a single coin to simplify handling of the
   *   fee
   */
  /// @param signature The signature by the current key for Serai's Ethereum validators
  /// @param coin The coin all of these `OutInstruction`s are for
  /// @param fee The fee to pay (in coin) to the caller for their relaying of this batch
  /// @param outs The `OutInstruction`s to act on
  function execute(
    Signature calldata signature,
    address coin,
    uint256 fee,
    OutInstruction[] calldata outs
  ) external;

  /// @notice Escapes to a new smart contract
  /// @dev This should be used upon an invariant being reached or new functionality being needed
  /// @param signature The signature by the current key for Serai's Ethereum validators
  /// @param escapeTo The address to escape to
  function escapeHatch(Signature calldata signature, address escapeTo) external;
}
