xDAI Price: $0.999902 (-0.02%)
Gas: 2.1 GWei

Contract

0x8A668fDE837AC420628f925012025Bae4201cbDC

Overview

xDAI Balance

Gnosis Chain LogoGnosis Chain LogoGnosis Chain Logo0 xDAI

xDAI Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
396916972025-04-22 13:45:3017 hrs ago1745329530  Contract Creation0 xDAI
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xD8ABc7DB...3323C7A01
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
OuterSpaceFleetsReadFacet

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 999999 runs

Other Settings:
london EvmVersion, GNU AGPLv3 license
File 1 of 18 : OuterSpaceFleetsReadFacet.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

import "./OuterSpaceFacetBase.sol";
import "../interfaces/IOuterSpaceFleetsRead.sol";

contract OuterSpaceFleetsReadFacet is OuterSpaceFacetBase, IOuterSpaceFleetsRead {
    // solhint-disable-next-line no-empty-blocks
    constructor(Config memory config) OuterSpaceFacetBase(config) {}

    function getFleet(
        uint256 fleetId,
        uint256 from
    )
        external
        view
        returns (
            address owner,
            uint40 launchTime,
            uint32 quantity,
            uint64 flyingAtLaunch, // can be more than quantity if multiple fleet were launched around the same time from the same planet
            uint64 destroyedAtLaunch
        )
    {
        Fleet memory fleet = _fleets[fleetId];
        launchTime = fleet.launchTime;
        quantity = fleet.quantity >> 31 == 1 ? 0 : fleet.quantity; // keep old behavior
        owner = fleet.owner;

        uint256 timeSlot = launchTime / (_frontrunningDelay / 2);
        destroyedAtLaunch = _inFlight[from][timeSlot].destroyed;
        flyingAtLaunch = _inFlight[from][timeSlot].flying;
    }

    function getFleetData(uint256 fleetId, uint256 from) external view returns (FleetData memory) {
        Fleet memory fleet = _fleets[fleetId];
        uint256 timeSlot = fleet.launchTime / (_frontrunningDelay / 2);

        return
            FleetData({
                arrived: fleet.quantity >> 31 == 1,
                owner: fleet.owner,
                launchTime: fleet.launchTime,
                quantity: fleet.quantity & 0x3FFFFFFF,
                destroyedAtLaunch: _inFlight[from][timeSlot].destroyed,
                flyingAtLaunch: _inFlight[from][timeSlot].flying,
                defender: fleet.defender,
                arrivalTime: fleet.arrivalTime,
                defenderLoss: fleet.defenderLoss,
                planetActive: fleet.planetActive,
                victory: fleet.victory
            });
    }
}

File 2 of 18 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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 3 of 18 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 4 of 18 : ECDSA.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s;
        uint8 v;
        assembly {
            s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
            v := add(shr(255, vs), 27)
        }
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 5 of 18 : Proxied.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

abstract contract Proxied {
    /// @notice to be used by initialisation / postUpgrade function so that only the proxy's admin can execute them
    /// It also allows these functions to be called inside a contructor
    /// even if the contract is meant to be used without proxy
    modifier proxied() {
        address proxyAdminAddress = _proxyAdmin();
        // With hardhat-deploy proxies
        // the proxyAdminAddress is zero only for the implementation contract
        // if the implementation contract want to be used as a standalone/immutable contract
        // it simply has to execute the `proxied` function
        // This ensure the proxyAdminAddress is never zero post deployment
        // And allow you to keep the same code for both proxied contract and immutable contract
        if (proxyAdminAddress == address(0)) {
            // ensure can not be called twice when used outside of proxy : no admin
            // solhint-disable-next-line security/no-inline-assembly
            assembly {
                sstore(
                    0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103,
                    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
                )
            }
        } else {
            require(msg.sender == proxyAdminAddress);
        }
        _;
    }

    modifier onlyProxyAdmin() {
        require(msg.sender == _proxyAdmin(), "NOT_AUTHORIZED");
        _;
    }

    function _proxyAdmin() internal view returns (address ownerAddress) {
        // solhint-disable-next-line security/no-inline-assembly
        assembly {
            ownerAddress := sload(0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103)
        }
    }
}

File 6 of 18 : console.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

library console {
    address constant CONSOLE_ADDRESS =
        0x000000000000000000636F6e736F6c652e6c6f67;

    function _sendLogPayloadImplementation(bytes memory payload) internal view {
        address consoleAddress = CONSOLE_ADDRESS;
        /// @solidity memory-safe-assembly
        assembly {
            pop(
                staticcall(
                    gas(),
                    consoleAddress,
                    add(payload, 32),
                    mload(payload),
                    0,
                    0
                )
            )
        }
    }

    function _castToPure(
      function(bytes memory) internal view fnIn
    ) internal pure returns (function(bytes memory) pure fnOut) {
        assembly {
            fnOut := fnIn
        }
    }

    function _sendLogPayload(bytes memory payload) internal pure {
        _castToPure(_sendLogPayloadImplementation)(payload);
    }

    function log() internal pure {
        _sendLogPayload(abi.encodeWithSignature("log()"));
    }

    function logInt(int256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
    }

    function logUint(uint256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
    }

    function logString(string memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function logBool(bool p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function logAddress(address p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function logBytes(bytes memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
    }

    function logBytes1(bytes1 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
    }

    function logBytes2(bytes2 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
    }

    function logBytes3(bytes3 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
    }

    function logBytes4(bytes4 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
    }

    function logBytes5(bytes5 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
    }

    function logBytes6(bytes6 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
    }

    function logBytes7(bytes7 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
    }

    function logBytes8(bytes8 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
    }

    function logBytes9(bytes9 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
    }

    function logBytes10(bytes10 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
    }

    function logBytes11(bytes11 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
    }

    function logBytes12(bytes12 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
    }

    function logBytes13(bytes13 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
    }

    function logBytes14(bytes14 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
    }

    function logBytes15(bytes15 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
    }

    function logBytes16(bytes16 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
    }

    function logBytes17(bytes17 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
    }

    function logBytes18(bytes18 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
    }

    function logBytes19(bytes19 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
    }

    function logBytes20(bytes20 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
    }

    function logBytes21(bytes21 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
    }

    function logBytes22(bytes22 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
    }

    function logBytes23(bytes23 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
    }

    function logBytes24(bytes24 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
    }

    function logBytes25(bytes25 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
    }

    function logBytes26(bytes26 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
    }

    function logBytes27(bytes27 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
    }

    function logBytes28(bytes28 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
    }

    function logBytes29(bytes29 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
    }

    function logBytes30(bytes30 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
    }

    function logBytes31(bytes31 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
    }

    function logBytes32(bytes32 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
    }

    function log(uint256 p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0));
    }

    function log(string memory p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string)", p0));
    }

    function log(bool p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
    }

    function log(address p0) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address)", p0));
    }

    function log(uint256 p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1));
    }

    function log(uint256 p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1));
    }

    function log(uint256 p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1));
    }

    function log(uint256 p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1));
    }

    function log(string memory p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
    }

    function log(string memory p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
    }

    function log(string memory p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
    }

    function log(string memory p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
    }

    function log(bool p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1));
    }

    function log(bool p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
    }

    function log(bool p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
    }

    function log(bool p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
    }

    function log(address p0, uint256 p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1));
    }

    function log(address p0, string memory p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
    }

    function log(address p0, bool p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
    }

    function log(address p0, address p1) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
    }

    function log(uint256 p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2));
    }

    function log(uint256 p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2));
    }

    function log(uint256 p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2));
    }

    function log(uint256 p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2));
    }

    function log(string memory p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
    }

    function log(string memory p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
    }

    function log(string memory p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
    }

    function log(string memory p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2));
    }

    function log(string memory p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
    }

    function log(string memory p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
    }

    function log(string memory p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2));
    }

    function log(bool p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
    }

    function log(bool p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
    }

    function log(bool p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2));
    }

    function log(bool p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
    }

    function log(bool p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
    }

    function log(bool p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
    }

    function log(bool p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2));
    }

    function log(bool p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
    }

    function log(bool p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
    }

    function log(bool p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2));
    }

    function log(address p0, uint256 p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2));
    }

    function log(address p0, string memory p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2));
    }

    function log(address p0, string memory p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
    }

    function log(address p0, string memory p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
    }

    function log(address p0, string memory p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
    }

    function log(address p0, bool p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2));
    }

    function log(address p0, bool p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
    }

    function log(address p0, bool p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
    }

    function log(address p0, bool p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
    }

    function log(address p0, address p1, uint256 p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2));
    }

    function log(address p0, address p1, string memory p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
    }

    function log(address p0, address p1, bool p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
    }

    function log(address p0, address p1, address p2) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3));
    }

    function log(uint256 p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
    }

    function log(string memory p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
    }

    function log(bool p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, uint256 p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, string memory p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, bool p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, uint256 p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, string memory p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, bool p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, uint256 p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, string memory p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, bool p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
    }

    function log(address p0, address p1, address p2, address p3) internal pure {
        _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
    }
}

File 7 of 18 : AllianceRegistry.sol
// SPDX-License-Identifier: AGPL-3.0

pragma solidity 0.8.9;

import "hardhat-deploy/solc_0.8/proxy/Proxied.sol";
import "../interfaces/IAlliance.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/Address.sol";

// import "hardhat/console.sol";

contract AllianceRegistry is Proxied {
    using ECDSA for bytes32;

    uint8 internal constant MAX_NUM_ALLIANCES = 4;

    mapping(address => mapping(IAlliance => uint256)) internal _allianceNonces;
    struct AllianceRow {
        IAlliance alliance;
        uint96 joinTime;
    }
    struct Alliances {
        AllianceRow alliance0;
        AllianceRow alliance1;
        AllianceRow alliance2;
        AllianceRow alliance3;
    }
    mapping(address => Alliances) internal _alliances;

    event AllianceLink(IAlliance indexed alliance, address indexed player, bool joining);

    function getAllianceDataAtSlot(address player, uint8 slot)
        external
        view
        returns (
            IAlliance alliance,
            uint96 joinTime,
            uint256 nonce
        )
    {
        Alliances storage alliances = _alliances[player];
        if (slot == 0) {
            alliance = alliances.alliance0.alliance;
            joinTime = alliances.alliance0.joinTime;
        } else if (slot == 1) {
            alliance = alliances.alliance1.alliance;
            joinTime = alliances.alliance1.joinTime;
        } else if (slot == 2) {
            alliance = alliances.alliance2.alliance;
            joinTime = alliances.alliance2.joinTime;
        } else if (slot == 3) {
            alliance = alliances.alliance3.alliance;
            joinTime = alliances.alliance3.joinTime;
        }

        nonce = _allianceNonces[player][alliance];
    }

    function getAllianceData(address player, IAlliance alliance) public view returns (uint96 joinTime, uint256 nonce) {
        nonce = _allianceNonces[player][alliance];

        Alliances storage alliances = _alliances[player];
        if (alliances.alliance0.alliance == alliance) {
            joinTime = alliances.alliance0.joinTime;
        } else if (alliances.alliance1.alliance == alliance) {
            joinTime = alliances.alliance1.joinTime;
        } else if (alliances.alliance2.alliance == alliance) {
            joinTime = alliances.alliance2.joinTime;
        } else if (alliances.alliance3.alliance == alliance) {
            joinTime = alliances.alliance3.joinTime;
        }
    }

    function havePlayersAnAllianceInCommon(
        address player1,
        address player2,
        uint256 timestamp
    ) external view returns (IAlliance alliance, uint96 joinTime) {
        Alliances storage p1Alliances = _alliances[player1];
        Alliances storage p2Alliances = _alliances[player2];

        AllianceRow[4] memory player1Alliances;
        AllianceRow[4] memory player2Alliances;
        uint256 num1 = 0;
        uint256 num2 = 0;

        for (uint256 i = 0; i < 4; i++) {
            if (i == num1) {
                AllianceRow memory allianceRow;
                if (i == 0) {
                    allianceRow = p1Alliances.alliance0;
                } else if (i == 1) {
                    allianceRow = p1Alliances.alliance1;
                } else if (i == 2) {
                    allianceRow = p1Alliances.alliance2;
                } else if (i == 3) {
                    allianceRow = p1Alliances.alliance3;
                }
                if (address(allianceRow.alliance) == address(0)) {
                    // console.log("p1 exhausted");
                    return (alliance, joinTime); // the alliance leave ensure that there is no gap // TODO
                }
                player1Alliances[num1++] = allianceRow;
            }
            for (uint256 j = 0; j < 4; j++) {
                if (j == num2) {
                    AllianceRow memory allianceRow;
                    if (j == 0) {
                        allianceRow = p2Alliances.alliance0;
                    } else if (j == 1) {
                        allianceRow = p2Alliances.alliance1;
                    } else if (j == 2) {
                        allianceRow = p2Alliances.alliance2;
                    } else if (j == 3) {
                        allianceRow = p2Alliances.alliance3;
                    }
                    if (address(allianceRow.alliance) == address(0)) {
                        // console.log("p2 exhausted");
                        // return (alliance, joinTime); // the alliance leave ensure that there is no gap // TODO
                        break;
                    }
                    player2Alliances[num2++] = allianceRow;
                }

                if (player1Alliances[i].alliance == player2Alliances[j].alliance) {
                    if (player1Alliances[i].joinTime >= player2Alliances[j].joinTime) {
                        if (player1Alliances[i].joinTime < timestamp) {
                            return (player1Alliances[i].alliance, player1Alliances[i].joinTime);
                        } else {
                            // TODO check greater ?
                            alliance = player1Alliances[i].alliance;
                            joinTime = player1Alliances[i].joinTime;
                        }
                    } else {
                        if (player2Alliances[j].joinTime < timestamp) {
                            return (player2Alliances[j].alliance, player2Alliances[j].joinTime);
                        } else {
                            // TODO check greater ?
                            alliance = player2Alliances[j].alliance;
                            joinTime = player2Alliances[j].joinTime;
                        }
                    }
                }
            }
        }
        // console.log(address(alliance));
        // console.log(joinTime);
    }

    // -----------------------------------------------------------------------------------------------------
    // FROM PLAYER
    // -----------------------------------------------------------------------------------------------------

    function joinAlliance(IAlliance alliance, bytes calldata data) external returns (bool joined) {
        Alliances storage alliances = _alliances[msg.sender];
        uint256 slot = 0;
        if (address(alliances.alliance0.alliance) != address(0)) {
            slot++;
        }
        if (address(alliances.alliance1.alliance) != address(0)) {
            slot++;
        }
        if (address(alliances.alliance2.alliance) != address(0)) {
            slot++;
        }
        require(address(alliances.alliance3.alliance) == address(0), "MAX_NUM_ALLIANCES_REACHED");

        joined = alliance.requestToJoin(msg.sender, data);
        if (joined) {
            if (slot == 0) {
                alliances.alliance0.alliance = alliance;
                alliances.alliance0.joinTime = uint96(block.timestamp);
            } else if (slot == 1) {
                alliances.alliance1.alliance = alliance;
                alliances.alliance1.joinTime = uint96(block.timestamp);
            } else if (slot == 2) {
                alliances.alliance2.alliance = alliance;
                alliances.alliance2.joinTime = uint96(block.timestamp);
            } else if (slot == 3) {
                alliances.alliance3.alliance = alliance;
                alliances.alliance3.joinTime = uint96(block.timestamp);
            }

            emit AllianceLink(alliance, msg.sender, true);
        }
    }

    function leaveAlliance(IAlliance alliance) external {
        _leaveAlliance(msg.sender, alliance);
        try alliance.playerHasLeft(msg.sender) {} catch {}
        // TODO ensure callback not failed due to low gas (1/64 rule)
    }

    // -----------------------------------------------------------------------------------------------------
    // FROM ALLIANCE
    // -----------------------------------------------------------------------------------------------------

    function addPlayerToAlliance(
        address player,
        uint32 nonce,
        bytes calldata signature
    ) external {
        _addPlayerToAlliance(player, nonce, signature);
    }

    struct PlayerSubmission {
        address addr;
        uint32 nonce;
        bytes signature;
    }

    function addMultiplePlayersToAlliance(PlayerSubmission[] calldata playerSubmissions) external {
        for (uint256 i = 0; i < playerSubmissions.length; i++) {
            _addPlayerToAlliance(playerSubmissions[i].addr, playerSubmissions[i].nonce, playerSubmissions[i].signature);
        }
    }

    function ejectPlayerFromAlliance(address player) external {
        _leaveAlliance(player, IAlliance(msg.sender));
    }

    // -----------------------------------------------------------------------------------------------------
    // INTERNAL
    // -----------------------------------------------------------------------------------------------------

    function _addPlayerToAlliance(
        address player,
        uint32 nonce,
        bytes calldata signature
    ) internal {
        IAlliance alliance = IAlliance(msg.sender);

        Alliances storage alliances = _alliances[player];
        uint256 slot = 0;
        if (address(alliances.alliance0.alliance) != address(0)) {
            require(alliances.alliance0.alliance != alliance, "ALREADY_JOINED");
            slot++;
        }
        if (address(alliances.alliance1.alliance) != address(0)) {
            require(alliances.alliance1.alliance != alliance, "ALREADY_JOINED");
            slot++;
        }
        if (address(alliances.alliance2.alliance) != address(0)) {
            require(alliances.alliance2.alliance != alliance, "ALREADY_JOINED");
            slot++;
        }
        require(alliances.alliance3.alliance != alliance, "ALREADY_JOINED");
        require(address(alliances.alliance3.alliance) == address(0), "MAX_NUM_ALLIANCES_REACHED");

        uint256 currentNonce = _allianceNonces[player][alliance];
        require(currentNonce == nonce, "INVALID_NONCE");

        bytes memory message;
        if (nonce == 0) {
            message = abi.encodePacked(
                "\x19Ethereum Signed Message:\n56",
                "Join Alliance 0x0000000000000000000000000000000000000000"
            );
            _writeUintAsHex(message, 28 + 55, uint160(msg.sender));
        } else {
            message = abi.encodePacked(
                "\x19Ethereum Signed Message:\n76",
                "Join Alliance 0x0000000000000000000000000000000000000000 (nonce:          0)"
            );
            _writeUintAsHex(message, 28 + 55, uint160(msg.sender));
            _writeUintAsDecimal(message, 28 + 74, nonce);
        }

        // console.log(string(message));

        bytes32 digest = keccak256(message);

        address signer = digest.recover(signature);
        require(player == signer, "INVALID_SIGNATURE");

        if (slot == 0) {
            alliances.alliance0.alliance = alliance;
            alliances.alliance0.joinTime = uint96(block.timestamp);
        } else if (slot == 1) {
            alliances.alliance1.alliance = alliance;
            alliances.alliance1.joinTime = uint96(block.timestamp);
        } else if (slot == 2) {
            alliances.alliance2.alliance = alliance;
            alliances.alliance2.joinTime = uint96(block.timestamp);
        } else if (slot == 3) {
            alliances.alliance3.alliance = alliance;
            alliances.alliance3.joinTime = uint96(block.timestamp);
        }
        _allianceNonces[player][alliance] = nonce + 1;

        emit AllianceLink(alliance, player, true);

        _checkERC1155AndCallSafeTransfer(msg.sender, address(0), player, uint256(uint160(address(alliance))), 1);
        emit TransferSingle(msg.sender, address(0), player, uint256(uint160(address(alliance))), 1);
    }

    bytes internal constant hexAlphabet = "0123456789abcdef";
    bytes internal constant decimalAlphabet = "0123456789";

    function _writeUintAsHex(
        bytes memory data,
        uint256 endPos,
        uint256 num
    ) internal pure {
        while (num != 0) {
            data[endPos--] = bytes1(hexAlphabet[num % 16]);
            num /= 16;
        }
    }

    function _writeUintAsDecimal(
        bytes memory data,
        uint256 endPos,
        uint256 num
    ) internal pure {
        while (num != 0) {
            data[endPos--] = bytes1(decimalAlphabet[num % 10]);
            num /= 10;
        }
    }

    function _leaveAlliance(address player, IAlliance alliance) internal {
        Alliances storage alliances = _alliances[player];

        IAlliance lastSlotAlliance;
        uint96 lastSlotJoinTime;

        require(address(alliances.alliance0.alliance) != address(0), "NOT_PART_OF_ANY_ALLIANCE");

        if (address(alliances.alliance1.alliance) == address(0)) {
            lastSlotAlliance = alliances.alliance0.alliance;
            lastSlotJoinTime = alliances.alliance0.joinTime;
            alliances.alliance0.alliance = IAlliance(address(0));
            alliances.alliance0.joinTime = 0;
        } else {
            if (address(alliances.alliance2.alliance) == address(0)) {
                lastSlotAlliance = alliances.alliance1.alliance;
                lastSlotJoinTime = alliances.alliance1.joinTime;
                alliances.alliance1.alliance = IAlliance(address(0));
                alliances.alliance1.joinTime = 0;
            } else {
                if (address(alliances.alliance3.alliance) == address(0)) {
                    lastSlotAlliance = alliances.alliance2.alliance;
                    lastSlotJoinTime = alliances.alliance2.joinTime;
                    alliances.alliance2.alliance = IAlliance(address(0));
                    alliances.alliance2.joinTime = 0;
                } else {
                    lastSlotAlliance = alliances.alliance3.alliance;
                    lastSlotJoinTime = alliances.alliance3.joinTime;
                    alliances.alliance3.alliance = IAlliance(address(0));
                    alliances.alliance3.joinTime = 0;
                }
            }
        }

        if (alliance != lastSlotAlliance) {
            if (alliances.alliance0.alliance == alliance) {
                alliances.alliance0.alliance = lastSlotAlliance;
                alliances.alliance0.joinTime = lastSlotJoinTime;
            } else if (alliances.alliance1.alliance == alliance) {
                alliances.alliance1.alliance = lastSlotAlliance;
                alliances.alliance1.joinTime = lastSlotJoinTime;
            } else if (alliances.alliance2.alliance == alliance) {
                alliances.alliance2.alliance = lastSlotAlliance;
                alliances.alliance2.joinTime = lastSlotJoinTime;
            } else {
                revert("NOT_PART_OF_THE_ALLIANCE");
            }
        }

        emit AllianceLink(alliance, player, false);
        emit TransferSingle(msg.sender, player, address(0), uint256(uint160(address(alliance))), 1);
    }

    function _msgSender() internal view returns (address) {
        return msg.sender; // TODO metatx
    }

    // ---------------------------------------------------------------------
    // Support For ERC-1155
    // ---------------------------------------------------------------------

    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    function balanceOf(address owner, uint256 id) external view returns (uint256 balance) {
        require(id == uint160(id), "INVALID_ID");
        (uint96 joinTime, ) = getAllianceData(owner, IAlliance(address(uint160(id))));
        if (joinTime > 0) {
            return 1;
        } else {
            return 0;
        }
    }

    function balanceOfBatch(address[] calldata owners, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory balances)
    {
        balances = new uint256[](owners.length);
        for (uint256 i = 0; i < owners.length; i++) {
            require(ids[i] == uint160(ids[i]), "INVALID_ID");
            (uint96 joinTime, ) = getAllianceData(owners[i], IAlliance(address(uint160(ids[i]))));
            if (joinTime > 0) {
                balances[i] = 1;
            } else {
                balances[i] = 0;
            }
        }
    }

    function isApprovedForAll(address, address) external pure returns (bool) {
        return false;
    }

    function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
        return interfaceID == 0xd9b67a26 || interfaceID == 0x01ffc9a7;
    }

    function _checkERC1155AndCallSafeTransfer(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 value
    ) internal returns (bool) {
        if (!Address.isContract(to)) {
            return true;
        }

        return ERC1155TokenReceiver(to).onERC1155Received(operator, from, id, value, "") == 0xf23a6e61;
    }
}

interface ERC1155TokenReceiver {
    function onERC1155Received(
        address _operator,
        address _from,
        uint256 _id,
        uint256 _value,
        bytes calldata _data
    ) external returns (bytes4);
}

File 8 of 18 : IFreePlayToken.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IFreePlayToken is IERC20 {
    function mintViaNativeToken(address to, uint256 amount) external payable;

    function mintViaNativeTokenPlusSendExtraNativeTokens(address payable to, uint256 amount) external payable;

    function mintMultipleViaNativeTokenPlusSendExtraNativeTokens(
        address payable[] calldata tos,
        uint256[] calldata amounts,
        uint256[] calldata nativeTokenAmounts
    ) external payable;

    function mint(
        address from,
        address to,
        uint256 amount
    ) external;

    function burn(
        address from,
        address to,
        uint256 amount
    ) external;

    struct BurnFrom {
        address from;
        uint256 amount;
    }

    function burnMultiple(BurnFrom[] calldata list, address to) external;
}

File 9 of 18 : IAlliance.sol
// SPDX-License-Identifier: AGPL-3.0

pragma solidity 0.8.9;

interface IAlliance {
    function requestToJoin(address player, bytes calldata data) external returns (bool);

    function playerHasLeft(address player) external;
}

File 10 of 18 : Extraction.sol
// SPDX-License-Identifier: AGPL-3.0

pragma solidity 0.8.9;

// TODO remove
import "hardhat/console.sol";

library Extraction {
    function value(
        bytes32 data,
        uint8 leastSignificantBit,
        uint8 size
    ) internal pure returns (uint256) {
        return uint256((data >> leastSignificantBit)) % 2**size;
    }

    function value8Mod(
        bytes32 data,
        uint8 leastSignificantBit,
        uint8 mod
    ) internal pure returns (uint8) {
        return uint8(uint256((data >> leastSignificantBit)) % mod);
    }

    function value8(bytes32 data, uint8 leastSignificantBit) internal pure returns (uint8) {
        return uint8(uint256((data >> leastSignificantBit)) % 2**8);
    }

    // 1+1+2+3+4+6+7+8+8+7+6+4+3+2+1+1 // aproximation of normal distribution with mean=7.5 and standard deviation=3 for 16 values
    bytes32 constant n_m7_5_sd3 = 0x01223334444555555666666677777777888888889999999AAAAAABBBBCCCDDEF;

    function normal8(bytes32 data, uint8 leastSignificantBit) internal pure returns (uint8) {
        uint8 index = value8Mod(data, leastSignificantBit, 64);
        uint8 first = index / 2;
        uint8 second = index % 2;
        uint8 slot = uint8(n_m7_5_sd3[first]);
        if (second == 0) {
            return slot >> 4;
        } else {
            return slot % 16;
        }
    }

    function normal16(
        bytes32 data,
        uint8 leastSignificantBit,
        bytes32 selection
    ) internal pure returns (uint16) {
        uint8 index = normal8(data, leastSignificantBit);
        return uint16(uint8(selection[index * 2])) * 2**8 + uint16(uint8(selection[index * 2 + 1]));
    }
}

File 11 of 18 : Math.sol
// SPDX-License-Identifier: AGPL-3.0

pragma solidity 0.8.9;

library Math {
    function mul(
        uint256 a,
        uint256 b,
        string memory overflowError
    ) internal pure returns (uint256 c) {
        require(b == 0 || a == 0 || ((c = a * b) / b) == a, overflowError);
    }

    function add(
        uint256 a,
        uint256 b,
        string memory overflowError
    ) internal pure returns (uint256 c) {
        require((c = a + b) >= a, overflowError);
    }

    function sub(
        uint256 a,
        uint256 b,
        string memory underflowError
    ) internal pure returns (uint256 c) {
        require((c = a - b) <= a, underflowError);
    }

    function mul18(
        uint256 a18,
        uint256 b18,
        string memory overflowError
    ) internal pure returns (uint256) {
        return mul(a18, b18, overflowError) / 10**18;
    }

    function div18(
        uint256 a18,
        uint256 b18,
        string memory overflowError
    ) internal pure returns (uint256) {
        return mul(a18, 10**18, overflowError) / b18;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a <= b ? a : b;
    }

    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    function smin(int256 a, int256 b) internal pure returns (int256) {
        return a <= b ? a : b;
    }

    function smax(int256 a, int256 b) internal pure returns (int256) {
        return a >= b ? a : b;
    }

    function sqrt(uint256 a) internal pure returns (uint256 c) {
        uint256 tmp = (a + 1) / 2;
        c = a;
        while (tmp < c) {
            c = tmp;
            tmp = ((a / tmp) + tmp) / 2;
        }
    }
}

File 12 of 18 : ImportingOuterSpaceConstants.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

contract ImportingOuterSpaceConstants {
    uint256 internal constant DECIMALS_18 = 1e18;
    uint256 internal constant DECIMALS_14 = 1e14;
    uint32 internal constant ACTIVE_MASK = 2**31;
    int256 internal constant UINT32_MAX = 2**32 - 1;
    int256 internal constant UINT32_MIN = -2147483648;
}

File 13 of 18 : UsingOuterSpaceDataLayout.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

import "../types/ImportingOuterSpaceTypes.sol";

contract UsingOuterSpaceDataLayout is ImportingOuterSpaceTypes {
    mapping(uint256 => Planet) internal _planets;
    mapping(uint256 => Fleet) internal _fleets;

    mapping(address => uint256) internal _stakeReadyToBeWithdrawn;

    mapping(address => mapping(address => bool)) internal _operators;

    // Note: make it namespaces per user, currently it is possible (though unlikely) for 2 users to share a slot if one attack another and quickly send away spaceships
    mapping(uint256 => mapping(uint256 => InFlight)) internal _inFlight;

    Discovered internal _discovered;
    // rewards
    mapping(address => uint256) internal _prevRewardIds;
    mapping(uint256 => uint256) internal _rewards;
    mapping(address => mapping(uint256 => bool)) internal _rewardsToWithdraw;

    // This adds 20,000 gas to all resolution
    mapping(uint256 => mapping(address => mapping(uint256 => AccumulatedAttack))) internal _attacks;

    mapping(address => uint256) internal _freeStakeReadyToBeWithdrawn;
    mapping(uint256 => uint256) internal _planetFlagged;
}

File 14 of 18 : ImportingOuterSpaceEvents.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

interface ImportingOuterSpaceEvents {
    event BlockTime(uint256 block, uint256 timestamp);
    event PlanetStake(
        address indexed acquirer,
        uint256 indexed location,
        uint32 numSpaceships,
        int40 travelingUpkeep,
        uint32 overflow,
        uint256 stake,
        bool freegift
    );
    event FleetSent(
        address indexed fleetSender,
        address indexed fleetOwner,
        uint256 indexed from,
        address operator,
        uint256 fleet,
        uint32 quantity,
        uint32 newNumSpaceships,
        int40 newTravelingUpkeep,
        uint32 newOverflow
    );

    event FleetRevealed(
        uint256 indexed fleetId,
        uint256 indexed from,
        uint256 indexed to,
        uint256 arrivalTimeWanted,
        bool gift,
        address specific,
        bytes32 secret,
        address fleetSender,
        address operator
    );

    struct ArrivalData {
        uint32 newNumspaceships;
        int40 newTravelingUpkeep;
        uint32 newOverflow;
        uint32 numSpaceshipsAtArrival;
        uint32 taxLoss;
        uint32 fleetLoss;
        uint32 planetLoss;
        uint32 inFlightFleetLoss;
        uint32 inFlightPlanetLoss;
        uint32 accumulatedDefenseAdded;
        uint32 accumulatedAttackAdded;
    }

    event FleetArrived(
        uint256 indexed fleet,
        address indexed fleetOwner,
        address indexed destinationOwner,
        uint256 destination,
        bool gift,
        bool won,
        ArrivalData data
    );

    event TravelingUpkeepRefund(
        uint256 indexed origin,
        uint256 indexed fleet,
        uint32 newNumspaceships,
        int40 newTravelingUpkeep,
        uint32 newOverflow
    );

    event PlanetTransfer(
        address indexed previousOwner,
        address indexed newOwner,
        uint256 indexed location,
        uint32 newNumspaceships,
        int40 newTravelingUpkeep,
        uint32 newOverflow
    );

    event PlanetReset(uint256 indexed location);

    event PlanetExit(address indexed owner, uint256 indexed location);

    event ExitComplete(address indexed owner, uint256 indexed location, uint256 stake);

    event RewardSetup(uint256 indexed location, address indexed giver, uint256 rewardId);
    event RewardToWithdraw(address indexed owner, uint256 indexed location, uint256 indexed rewardId);

    event StakeToWithdraw(address indexed owner, uint256 newStake, bool freegift);

    event Initialized(
        bytes32 genesis,
        uint256 resolveWindow,
        uint256 timePerDistance,
        uint256 exitDuration,
        uint32 acquireNumSpaceships,
        uint32 productionSpeedUp,
        uint256 frontrunningDelay,
        uint256 productionCapAsDuration,
        uint256 upkeepProductionDecreaseRatePer10000th,
        uint256 fleetSizeFactor6,
        uint32 initialSpaceExpansion,
        uint32 expansionDelta,
        uint256 giftTaxPer10000
    );

    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    event Transfer(address indexed from, address indexed to, uint256 indexed location);

    event GeneratorChanged(address newGenerator);
    event GeneratorAdminChanged(address newGeneratorAdmin);
}

File 15 of 18 : OuterSpaceFacetBase.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

import "../types/ImportingOuterSpaceTypes.sol";
import "../base/ImportingOuterSpaceConstants.sol";
import "../events/ImportingOuterSpaceEvents.sol";
import "../base/UsingOuterSpaceDataLayout.sol";

import "../../libraries/Extraction.sol";
import "../../libraries/Math.sol";

import "../../interfaces/IAlliance.sol";
import "../../alliances/AllianceRegistry.sol";

import "../../conquest_token/IFreePlayToken.sol";

interface StakingToken is IERC20 {
    function mint(address to, uint256 amount) external payable;
}

contract OuterSpaceFacetBase is
    ImportingOuterSpaceTypes,
    ImportingOuterSpaceConstants,
    ImportingOuterSpaceEvents,
    UsingOuterSpaceDataLayout
{
    using Extraction for bytes32;

    StakingToken internal immutable _stakingToken;
    IFreePlayToken internal immutable _freeStakingToken;
    AllianceRegistry internal immutable _allianceRegistry;

    bytes32 internal immutable _genesis;
    uint256 internal immutable _resolveWindow;
    uint256 internal immutable _timePerDistance;
    uint256 internal immutable _exitDuration;
    uint32 internal immutable _acquireNumSpaceships; // TODO use uint256
    uint32 internal immutable _productionSpeedUp; // TODO use uint256
    uint256 internal immutable _frontrunningDelay;
    uint256 internal immutable _productionCapAsDuration;
    uint256 internal immutable _upkeepProductionDecreaseRatePer10000th;
    uint256 internal immutable _fleetSizeFactor6;
    uint32 internal immutable _initialSpaceExpansion; // = 16;
    uint32 internal immutable _expansionDelta; // = 8;  // TODO use uint256
    uint256 internal immutable _giftTaxPer10000; // = 2500;
    // // 4,5,5,10,10,15,15, 20, 20, 30,30,40,40,80,80,100
    // bytes32 constant stakeRange = 0x000400050005000A000A000F000F00140014001E001E00280028005000500064;
    // 6, 8, 10, 12, 14, 16, 18, 20, 20, 22, 24, 32, 40, 48, 56, 72
    // bytes32 internal constant stakeRange = 0x00060008000A000C000E00100012001400140016001800200028003000380048;
    bytes32 internal immutable _stakeRange;
    uint256 internal immutable _stakeMultiplier10000th;
    uint256 internal immutable _bootstrapSessionEndTime;
    uint256 internal immutable _infinityStartTime;

    struct Config {
        StakingToken stakingToken;
        IFreePlayToken freeStakingToken;
        AllianceRegistry allianceRegistry;
        bytes32 genesis;
        uint256 resolveWindow;
        uint256 timePerDistance;
        uint256 exitDuration;
        uint32 acquireNumSpaceships;
        uint32 productionSpeedUp;
        uint256 frontrunningDelay;
        uint256 productionCapAsDuration;
        uint256 upkeepProductionDecreaseRatePer10000th;
        uint256 fleetSizeFactor6;
        uint32 initialSpaceExpansion;
        uint32 expansionDelta;
        uint256 giftTaxPer10000;
        bytes32 stakeRange;
        uint256 stakeMultiplier10000th;
        uint256 bootstrapSessionEndTime;
        uint256 infinityStartTime;
    }

    constructor(Config memory config) {
        uint32 t = uint32(config.timePerDistance) / 4; // the coordinates space is 4 times bigger
        require(t * 4 == config.timePerDistance, "TIME_PER_DIST_NOT_DIVISIBLE_4");

        _stakingToken = config.stakingToken;
        _freeStakingToken = config.freeStakingToken;
        _allianceRegistry = config.allianceRegistry;

        _genesis = config.genesis;
        _resolveWindow = config.resolveWindow;
        _timePerDistance = t;
        _exitDuration = config.exitDuration;
        _acquireNumSpaceships = config.acquireNumSpaceships;
        _productionSpeedUp = config.productionSpeedUp;
        _frontrunningDelay = config.frontrunningDelay;
        _productionCapAsDuration = config.productionCapAsDuration;
        _upkeepProductionDecreaseRatePer10000th = config.upkeepProductionDecreaseRatePer10000th;
        _fleetSizeFactor6 = config.fleetSizeFactor6;
        _initialSpaceExpansion = config.initialSpaceExpansion;
        _expansionDelta = config.expansionDelta;
        _giftTaxPer10000 = config.giftTaxPer10000;
        _stakeRange = config.stakeRange;
        _stakeMultiplier10000th = config.stakeMultiplier10000th;
        _bootstrapSessionEndTime = config.bootstrapSessionEndTime;
        _infinityStartTime = config.infinityStartTime;
    }

    // ---------------------------------------------------------------------------------------------------------------
    // PLANET STATE
    // ---------------------------------------------------------------------------------------------------------------

    struct PlanetUpdateState {
        uint256 location;
        uint40 lastUpdated;
        bool active; // modified
        uint32 numSpaceships; // modified
        int40 travelingUpkeep; // modified
        uint40 exitStartTime;
        uint40 newExitStartTime; // modified
        uint32 overflow; // modified
        address owner;
        address newOwner; // modified
        bytes32 data;
        uint24 futureExtraProduction;
    }

    function _createPlanetUpdateState(
        Planet memory planet,
        uint256 location
    ) internal view returns (PlanetUpdateState memory planetUpdate) {
        (bool active, uint32 currentNumSpaceships) = _activeNumSpaceships(planet.numSpaceships);
        planetUpdate.location = location;
        planetUpdate.lastUpdated = planet.lastUpdated;
        planetUpdate.active = active;
        planetUpdate.numSpaceships = currentNumSpaceships;
        planetUpdate.travelingUpkeep = planet.travelingUpkeep;
        planetUpdate.exitStartTime = planet.exitStartTime;
        planetUpdate.newExitStartTime = planet.exitStartTime;
        planetUpdate.overflow = planet.overflow;
        planetUpdate.owner = planet.owner;
        planetUpdate.newOwner = planet.owner;
        planetUpdate.data = _planetData(location);
    }

    // solhint-disable-next-line code-complexity
    function _computePlanetUpdateForTimeElapsed(PlanetUpdateState memory planetUpdate) internal view {
        if (planetUpdate.exitStartTime != 0) {
            if (_hasJustExited(planetUpdate.exitStartTime)) {
                planetUpdate.newExitStartTime = 0;
                planetUpdate.numSpaceships = 0;
                planetUpdate.travelingUpkeep = 0;
                planetUpdate.newOwner = address(0);
                planetUpdate.overflow = 0;
                planetUpdate.active = false; // event is emitted at the endof each write function
                // lastUpdated is set at the end directly on storage
                return;
            }
        }

        uint256 timePassed = block.timestamp - planetUpdate.lastUpdated;
        uint16 production = _production(planetUpdate.data);
        uint256 amountProducedTheWholeTime = (timePassed * uint256(_productionSpeedUp) * uint256(production)) / 1 hours;

        uint256 newNumSpaceships = planetUpdate.numSpaceships;
        uint256 extraUpkeepPaid = 0;
        if (_productionCapAsDuration > 0) {
            uint256 capWhenActive = _capWhenActive(production);
            uint256 cap = planetUpdate.active ? capWhenActive : 0;

            if (newNumSpaceships > cap) {
                uint256 decreaseRate = 1800;
                if (planetUpdate.overflow > 0) {
                    decreaseRate = (uint256(planetUpdate.overflow) * 1800) / capWhenActive;
                    if (decreaseRate < 1800) {
                        decreaseRate = 1800;
                    }
                }

                uint256 decrease = (timePassed * uint256(_productionSpeedUp) * decreaseRate) / 1 hours;
                if (decrease == 0) {
                    // NOTE: To ensure a player cannot simply ping the planet continuously to avoid the decrease
                    decrease = 1;
                }
                if (decrease > newNumSpaceships - cap) {
                    decrease = newNumSpaceships - cap;
                }

                if (planetUpdate.active) {
                    extraUpkeepPaid = decrease;
                }
                newNumSpaceships -= decrease;
            } else {
                if (planetUpdate.active) {
                    uint256 increase = amountProducedTheWholeTime;
                    if (planetUpdate.travelingUpkeep > 0) {
                        uint256 timeBeforeUpkeepBackToZero = (uint256(uint40(planetUpdate.travelingUpkeep)) * 1 hours) /
                            ((uint256(_productionSpeedUp) *
                                uint256(production) *
                                _upkeepProductionDecreaseRatePer10000th) / 10000); // 10,000 should be extracted as to not reach div by zero (like "1 hours")
                        if (timeBeforeUpkeepBackToZero >= timePassed) {
                            extraUpkeepPaid = increase;
                        } else {
                            extraUpkeepPaid =
                                (timeBeforeUpkeepBackToZero * uint256(_productionSpeedUp) * uint256(production)) /
                                1 hours;
                            if (extraUpkeepPaid > increase) {
                                extraUpkeepPaid = increase; // TODO remove ? should not be possible
                            }
                        }
                        increase -= extraUpkeepPaid;
                    }

                    uint256 maxIncrease = cap - newNumSpaceships;
                    if (increase > maxIncrease) {
                        extraUpkeepPaid += increase - maxIncrease;
                        increase = maxIncrease;
                    }
                    newNumSpaceships += increase;
                    // solhint-disable-next-line no-empty-blocks
                } else {
                    // not effect currently, when inactive, cap == 0, meaning zero spaceship here
                    // NOTE: we could do the following assuming we act on upkeepRepaid when inactive, we do not do that currently
                    //  extraUpkeepPaid = amountProducedTheWholeTime - upkeepRepaid;
                }
            }

            if (planetUpdate.active) {
                uint256 upkeepRepaid = ((amountProducedTheWholeTime * _upkeepProductionDecreaseRatePer10000th) /
                    10000) + extraUpkeepPaid;
                int256 newTravelingUpkeep = int256(planetUpdate.travelingUpkeep) - int40(uint40(upkeepRepaid));

                if (newTravelingUpkeep < -int256(cap)) {
                    newTravelingUpkeep = -int256(cap);
                }
                planetUpdate.travelingUpkeep = int40(newTravelingUpkeep);
            }
        } else {
            // TODO We are not using this branch, and in that branch there is no upkeep or overflow to consider
            if (planetUpdate.active) {
                newNumSpaceships += amountProducedTheWholeTime;
            } else {
                // NOTE no need to overflow here  as there is no production cap, so no incentive to regroup spaceships
                uint256 decrease = (timePassed * uint256(_productionSpeedUp) * 1800) / 1 hours;
                if (decrease > newNumSpaceships) {
                    decrease = newNumSpaceships;
                    newNumSpaceships = 0;
                } else {
                    newNumSpaceships -= decrease;
                }
            }
        }

        if (newNumSpaceships >= ACTIVE_MASK) {
            newNumSpaceships = ACTIVE_MASK - 1;
        }
        planetUpdate.numSpaceships = uint32(newNumSpaceships);

        if (!planetUpdate.active && planetUpdate.numSpaceships == 0) {
            planetUpdate.newOwner = address(0);
        }
    }

    function _setPlanet(Planet storage planet, PlanetUpdateState memory planetUpdate, bool exitInterupted) internal {
        if (planetUpdate.exitStartTime > 0 && planetUpdate.newExitStartTime == 0) {
            // NOTE: planetUpdate.newExitStartTime is only set to zero when exit is actually complete (not interupted)
            //  interuption is handled by exitInterupted
            // exit has completed, newExitStartTime is not set to zero for interuption,
            // interuption is taken care below (owner changes)
            _handleExitComplete(planetUpdate);
        }
        if (planetUpdate.owner != planetUpdate.newOwner) {
            planet.owner = planetUpdate.newOwner;
            if (planetUpdate.newOwner != address(0)) {
                planet.ownershipStartTime = uint40(block.timestamp);
            } else {
                planet.ownershipStartTime = 0;
            }
            emit Transfer(planetUpdate.owner, planetUpdate.newOwner, planetUpdate.location);
        }

        if (exitInterupted) {
            // if (planetUpdate.newExitStartTime == 0 && planetUpdate.exitStartTime > 0) {
            // exit interupted // TODO event ?
            // }
            planet.exitStartTime = 0;
        } else if (planetUpdate.newExitStartTime != planetUpdate.exitStartTime) {
            planet.exitStartTime = planetUpdate.newExitStartTime;
        }

        planet.numSpaceships = _setActiveNumSpaceships(planetUpdate.active, planetUpdate.numSpaceships);
        planet.travelingUpkeep = planetUpdate.travelingUpkeep;

        planet.overflow = planetUpdate.overflow;
        planet.lastUpdated = uint40(block.timestamp);
    }

    // ---------------------------------------------------------------------------------------------------------------
    // STAKING / PRODUCTION CAPTURE
    // ---------------------------------------------------------------------------------------------------------------

    function _acquire(address player, uint256 stake, uint256 location, bool freegift) internal whenNotPaused {
        // -----------------------------------------------------------------------------------------------------------
        // Initialise State Update
        // -----------------------------------------------------------------------------------------------------------
        Planet storage planet = _getPlanet(location);
        PlanetUpdateState memory planetUpdate = _createPlanetUpdateState(planet, location);

        // -----------------------------------------------------------------------------------------------------------
        // check requirements
        // -----------------------------------------------------------------------------------------------------------
        require(stake == uint256(_stake(planetUpdate.data)) * (DECIMALS_14), "INVALID_STAKE_AMOUNT");

        // -----------------------------------------------------------------------------------------------------------
        // Compute Basic Planet Updates
        // -----------------------------------------------------------------------------------------------------------
        _computePlanetUpdateForTimeElapsed(planetUpdate);

        // -----------------------------------------------------------------------------------------------------------
        // Staking logic...
        // -----------------------------------------------------------------------------------------------------------
        _computePlanetUpdateForStaking(player, planetUpdate);

        // -----------------------------------------------------------------------------------------------------------
        // Write New State
        // -----------------------------------------------------------------------------------------------------------
        _setPlanet(planet, planetUpdate, false);
        // _setAccountFromPlanetUpdate(planetUpdate);

        // -----------------------------------------------------------------------------------------------------------
        // Update Space Discovery
        // -----------------------------------------------------------------------------------------------------------
        _setDiscoveryAfterStaking(location);

        if (freegift) {
            _planetFlagged[location] = block.timestamp;
        } else {
            _planetFlagged[location] = 0; // staked with normal tokens
        }

        // -----------------------------------------------------------------------------------------------------------
        // Emit Event
        // -----------------------------------------------------------------------------------------------------------
        emit BlockTime(block.number, block.timestamp);
        emit PlanetStake(
            player,
            location,
            planetUpdate.numSpaceships,
            planetUpdate.travelingUpkeep,
            planetUpdate.overflow,
            stake,
            freegift
        );
        _notifyGeneratorAdd(planetUpdate.newOwner, stake);
    }

    function _computePlanetUpdateForStaking(address player, PlanetUpdateState memory planetUpdate) internal view {
        require(!planetUpdate.active, "STILL_ACTIVE");

        uint32 defense;
        // NOTE : natives are back automatically once spaceships reaches zero (here we know we are not active)
        // TODO consider making natives come back over time => would need to compute the time numSpaceship became zero
        if (planetUpdate.numSpaceships == 0) {
            defense = _natives(planetUpdate.data);
        } else {
            // Do not allow staking over occupied planets, they are going to zero at some point though
            require(planetUpdate.owner == player, "OCCUPIED");
        }

        uint16 production = _production(planetUpdate.data);
        uint32 cap = uint32(_capWhenActive(production));

        // We need to  ensure a player staking on a planet it previously exited work here
        planetUpdate.newOwner = player;
        if (defense != 0) {
            (uint32 attackerLoss, ) = _computeFight(
                uint256(_acquireNumSpaceships),
                defense,
                10000,
                _defense(planetUpdate.data)
            );
            // attacker alwasy win as defense (and stats.native) is restricted to 3500
            // (attackerLoss: 0, defenderLoss: 0) would mean defense was zero
            require(attackerLoss < _acquireNumSpaceships, "FAILED_CAPTURED");
            planetUpdate.numSpaceships = _acquireNumSpaceships - attackerLoss;

            // NOTE cannot be overflow here as staking provide a number of spaceships below that
            planetUpdate.overflow = 0;
        } else {
            planetUpdate.numSpaceships += _acquireNumSpaceships;
            if (_productionCapAsDuration > 0) {
                if (planetUpdate.numSpaceships > cap) {
                    planetUpdate.overflow = planetUpdate.numSpaceships - cap;
                } else {
                    planetUpdate.overflow = 0;
                }
            }
        }

        // NOTE when staking on a planet, we set an allowance for traveling upkeep
        planetUpdate.travelingUpkeep =
            -int32(uint32((uint256(cap) * _upkeepProductionDecreaseRatePer10000th) / 10000)) -
            int32(planetUpdate.numSpaceships);
        planetUpdate.active = true;
    }

    // solhint-disable-next-line code-complexity
    function _setDiscoveryAfterStaking(uint256 location) internal {
        Discovered memory discovered = _discovered;

        int256 x = int256(int128(int256(location & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
        int256 y = int256(int128(int256(location >> 128)));

        bool changes = false;
        if (x < 0) {
            require(-x <= int256(uint256(discovered.minX)), "NOT_REACHABLE_YET_MINX");
            x = -x + int32(_expansionDelta);
            if (x > UINT32_MAX) {
                x = UINT32_MAX;
            }
            if (int256(uint256(discovered.minX)) < x) {
                discovered.minX = uint32(uint256(x));
                changes = true;
            }
        } else {
            require(x <= int256(uint256(discovered.maxX)), "NOT_REACHABLE_YET_MAXX");
            x = x + int32(_expansionDelta);
            if (x > UINT32_MAX) {
                x = UINT32_MAX;
            }
            if (discovered.maxX < uint32(uint256(x))) {
                discovered.maxX = uint32(uint256(x));
                changes = true;
            }
        }

        if (y < 0) {
            require(-y <= int256(uint256(discovered.minY)), "NOT_REACHABLE_YET_MINY");
            y = -y + int32(_expansionDelta);
            if (y > UINT32_MAX) {
                y = UINT32_MAX;
            }
            if (int256(uint256(discovered.minY)) < y) {
                discovered.minY = uint32(uint256(y));
                changes = true;
            }
        } else {
            require(y <= int256(uint256(discovered.maxY)), "NOT_REACHABLE_YET_MAXY");
            y = y + int32(_expansionDelta);
            if (y > UINT32_MAX) {
                y = UINT32_MAX;
            }
            if (int256(uint256(discovered.maxY)) < y) {
                discovered.maxY = uint32(uint256(y));
                changes = true;
            }
        }
        if (changes) {
            _discovered = discovered;
        }
    }

    // ---------------------------------------------------------------------------------------------------------------
    // EXITS / WITHDRAWALS
    // ---------------------------------------------------------------------------------------------------------------

    function _handleExitComplete(PlanetUpdateState memory planetUpdate) internal {
        uint256 stake = _completeExit(planetUpdate.owner, planetUpdate.location, planetUpdate.data);

        // Note we could Transfer to zero and Transfer from zero ?

        // optional so we can use it in the batch withdraw,

        uint256 flagTime = _planetFlagged[planetUpdate.location];
        if (flagTime > 0) {
            // TODO reactivate once we siwtch to a fixed FreePlayToken
            // if (planetUpdate.exitStartTime >= flagTime + (6 days / _productionSpeedUp)) {
            //     _freeStakingToken.burn(address(this), address(this), stake);
            //     uint256 newStake = _stakeReadyToBeWithdrawn[planetUpdate.owner] + stake;
            //     _stakeReadyToBeWithdrawn[planetUpdate.owner] = newStake;
            //     emit StakeToWithdraw(planetUpdate.owner, newStake, false);
            // } else {
            uint256 newStake = _freeStakeReadyToBeWithdrawn[planetUpdate.owner] + stake;
            _freeStakeReadyToBeWithdrawn[planetUpdate.owner] = newStake;
            emit StakeToWithdraw(planetUpdate.owner, newStake, true);
            // }
        } else {
            uint256 newStake = _stakeReadyToBeWithdrawn[planetUpdate.owner] + stake;
            _stakeReadyToBeWithdrawn[planetUpdate.owner] = newStake;
            emit StakeToWithdraw(planetUpdate.owner, newStake, false);
        }
    }

    function _completeExit(address owner, uint256 location, bytes32 data) internal returns (uint256 stake) {
        stake = uint256(_stake(data)) * (DECIMALS_14);
        emit BlockTime(block.number, block.timestamp);
        emit ExitComplete(owner, location, stake);

        // --------------------------------------------------------
        // Extra Reward was added
        // --------------------------------------------------------
        uint256 rewardId = _rewards[location];
        if (rewardId != 0) {
            // rewardId would contains the package. maybe this could be handled by an external contract
            _rewardsToWithdraw[owner][rewardId] = true;
            _rewards[location] = 0; // reset
            // if you had reward to a planet in he process of exiting,
            // you are adding the reward to the player exiting unless _setPlanetAfterExit is called first
            emit RewardToWithdraw(owner, location, rewardId);
        }
        // --------------------------------------------------------
    }

    function _unsafe_exit_for(address owner, uint256 location) internal {
        Planet storage planet = _getPlanet(location);
        (bool active, ) = _activeNumSpaceships(planet.numSpaceships);
        require(active, "NOT_ACTIVE");
        require(owner == planet.owner, "NOT_OWNER");
        require(planet.exitStartTime == 0, "EXITING_ALREADY");

        planet.exitStartTime = uint40(block.timestamp);
        emit BlockTime(block.number, block.timestamp);
        emit PlanetExit(owner, location);

        // stake is removed as soon as we start exist
        // If the exit is interupted, it is given to the player interupting
        _notifyGeneratorRemove(owner, uint256(_stake(_planetData(location))) * (DECIMALS_14));
    }

    function _fetchAndWithdrawFor(address owner, uint256[] calldata locations) internal {
        uint256 addedStake = 0;
        uint256 freeAddedStake = 0;
        for (uint256 i = 0; i < locations.length; i++) {
            Planet storage planet = _getPlanet(locations[i]);
            if (_hasJustExited(planet.exitStartTime)) {
                require(owner == planet.owner, "NOT_OWNER");
                emit Transfer(owner, address(0), locations[i]);

                uint256 flagTime = _planetFlagged[locations[i]];
                if (flagTime > 0) {
                    // TODO reactivate once we siwtch to a fixed FreePlayToken
                    // if (planet.exitStartTime >= flagTime + (6 days / _productionSpeedUp)) {
                    //     uint256 extra = _completeExit(planet.owner, locations[i], _planetData(locations[i]));
                    //     addedStake += extra;
                    //     _freeStakingToken.burn(address(this), address(this), extra);
                    // } else {
                    freeAddedStake += _completeExit(planet.owner, locations[i], _planetData(locations[i]));
                    // }
                } else {
                    addedStake += _completeExit(planet.owner, locations[i], _planetData(locations[i]));
                }

                planet.owner = address(0);
                planet.ownershipStartTime = 0;
                planet.exitStartTime = 0;
                planet.numSpaceships = 0;
                planet.overflow = 0;
                planet.travelingUpkeep = 0;
                planet.lastUpdated = uint40(block.timestamp);
            }
        }
        uint256 newStake = _stakeReadyToBeWithdrawn[owner] + addedStake;
        _unsafe_withdrawAll(owner, newStake);

        uint256 newFreeStake = _freeStakeReadyToBeWithdrawn[owner] + freeAddedStake;
        _free_unsafe_withdrawAll(owner, newFreeStake);
    }

    function _unsafe_withdrawAll(address owner, uint256 amount) internal {
        _stakeReadyToBeWithdrawn[owner] = 0;
        emit StakeToWithdraw(owner, amount, false);
        require(_stakingToken.transfer(owner, amount), "FAILED_TRANSFER");
        emit StakeToWithdraw(owner, 0, false);
    }

    function _free_unsafe_withdrawAll(address owner, uint256 amount) internal {
        _freeStakeReadyToBeWithdrawn[owner] = 0;
        emit StakeToWithdraw(owner, amount, true);
        require(_freeStakingToken.transfer(owner, amount), "FAILED_TRANSFER");
        emit StakeToWithdraw(owner, 0, true);
    }

    function _hasJustExited(uint40 exitTime) internal view returns (bool) {
        if (exitTime == 0) {
            return false;
        }
        uint256 timestamp = block.timestamp;
        if (_bootstrapSessionEndTime > 0 && timestamp >= _bootstrapSessionEndTime && exitTime < _infinityStartTime) {
            return true;
        }

        return timestamp > exitTime + _exitDuration;
    }

    // ---------------------------------------------------------------------------------------------------------------
    // REWARDS
    // ---------------------------------------------------------------------------------------------------------------

    function _addReward(uint256 location, address sponsor) internal {
        uint256 rewardId = _rewards[location];
        require(rewardId == 0, "REWARD_ALREADY_AT_THIS_LOCATION");
        // TODO ?
        // Planet storage planet = _getPlanet(location);
        // require(planet.lastUpdated == 0, "PLANET_ALREADY_COLONIZED");
        rewardId = ++_prevRewardIds[sponsor];
        _rewards[location] = (uint256(uint160(sponsor)) << 96) + rewardId;
        emit RewardSetup(location, sponsor, rewardId);
    }

    // ---------------------------------------------------------------------------------------------------------------
    // FLEET SENDING
    // ---------------------------------------------------------------------------------------------------------------

    function _unsafe_sendFor(uint256 fleetId, address operator, FleetLaunch memory launch) internal whenNotPaused {
        // -----------------------------------------------------------------------------------------------------------
        // Initialise State Update
        // -----------------------------------------------------------------------------------------------------------
        Planet storage planet = _getPlanet(launch.from);
        PlanetUpdateState memory planetUpdate = _createPlanetUpdateState(planet, launch.from);

        // -----------------------------------------------------------------------------------------------------------
        // check requirements
        // -----------------------------------------------------------------------------------------------------------

        require(launch.quantity < 2 ** 30, "TOO_MANY_SPACESHIPS"); // only 2^30 because the first 2 bits = resolution
        require(launch.quantity > 0, "NO_SPACESHIPS");
        require(planet.exitStartTime == 0, "PLANET_EXIT");
        require(launch.fleetSender == planet.owner, "NOT_OWNER");

        // -----------------------------------------------------------------------------------------------------------
        // Compute Basic Planet Updates
        // -----------------------------------------------------------------------------------------------------------
        _computePlanetUpdateForTimeElapsed(planetUpdate);

        // -----------------------------------------------------------------------------------------------------------
        // Requirements post Planet Updates
        // -----------------------------------------------------------------------------------------------------------

        require(planetUpdate.numSpaceships >= launch.quantity, "SPACESHIPS_NOT_ENOUGH");

        // -----------------------------------------------------------------------------------------------------------
        // Sending logic...
        // -----------------------------------------------------------------------------------------------------------
        _computePlanetUpdateForFleetLaunch(planetUpdate, launch.quantity);

        // -----------------------------------------------------------------------------------------------------------
        // Write New State
        // -----------------------------------------------------------------------------------------------------------
        _setPlanet(planet, planetUpdate, false);
        // _setAccountFromPlanetUpdate(planetUpdate);

        _setFleetFlyingSlot(launch.from, launch.quantity);

        require(_fleets[fleetId].quantity == 0, "FLEET_EXISTS");
        _fleets[fleetId] = Fleet({
            launchTime: uint40(block.timestamp),
            owner: launch.fleetOwner,
            quantity: launch.quantity,
            futureExtraProduction: planetUpdate.futureExtraProduction,
            defender: address(0),
            arrivalTime: 0,
            defenderLoss: 0,
            victory: false,
            planetActive: false
        });

        emit BlockTime(block.number, block.timestamp);
        emit FleetSent(
            launch.fleetSender,
            launch.fleetOwner,
            launch.from,
            operator,
            fleetId,
            launch.quantity,
            planetUpdate.numSpaceships,
            planetUpdate.travelingUpkeep,
            planetUpdate.overflow
        );
    }

    function _computePlanetUpdateForFleetLaunch(PlanetUpdateState memory planetUpdate, uint32 quantity) internal view {
        planetUpdate.numSpaceships -= quantity;
        if (_productionCapAsDuration > 0) {
            if (planetUpdate.active) {
                // NOTE we do not update travelingUpkeep on Inactive planets
                //  these get reset on staking

                uint16 production = _production(planetUpdate.data);
                uint256 cap = _capWhenActive(production);
                if (planetUpdate.numSpaceships < cap) {
                    uint256 futureExtraProduction = cap - planetUpdate.numSpaceships;
                    if (futureExtraProduction > quantity) {
                        futureExtraProduction = quantity;
                    }
                    int256 newTravelingUpkeep = int256(planetUpdate.travelingUpkeep) + int256(futureExtraProduction);
                    if (newTravelingUpkeep > int256(cap)) {
                        newTravelingUpkeep = int256(cap);
                    }
                    planetUpdate.travelingUpkeep = int40(newTravelingUpkeep);
                    planetUpdate.futureExtraProduction = uint24(futureExtraProduction); // cap is always smaller than uint24
                }
            }

            if (planetUpdate.overflow > quantity) {
                planetUpdate.overflow -= quantity;
            } else {
                planetUpdate.overflow = 0;
            }
        }
    }

    function _setFleetFlyingSlot(uint256 from, uint32 quantity) internal {
        // -----------------------------------------------------------------------------------------------------------
        // record flying fleets (to prevent front-running, see resolution)
        // -----------------------------------------------------------------------------------------------------------
        uint256 timeSlot = block.timestamp / (_frontrunningDelay / 2);
        uint32 flying = _inFlight[from][timeSlot].flying;
        unchecked {
            flying = flying + quantity;
        }
        require(flying >= quantity, "ORBIT_OVERFLOW"); // unlikely to ever happen,
        // would need a huge amount of spaceships to be received and each in turn being sent
        // TOEXPLORE could also cap, that would result in some fleet being able to escape.
        _inFlight[from][timeSlot].flying = flying;
        // -----------------------------------------------------------------------------------------------------------
    }

    // ---------------------------------------------------------------------------------------------------------------
    // FLEET RESOLUTION, ATTACK / REINFORCEMENT
    // ---------------------------------------------------------------------------------------------------------------
    struct ResolutionState {
        address fleetOwner;
        uint40 fleetLaunchTime;
        uint32 originalQuantity;
        uint32 fleetQuantity;
        bytes32 fromData;
        uint32 inFlightFleetLoss;
        uint32 inFlightPlanetLoss;
        bool gifting;
        bool taxed;
        bool victory;
        uint32 attackerLoss;
        uint32 defenderLoss;
        uint32 orbitDefense1;
        uint32 orbitDefenseDestroyed1;
        uint32 orbitDefense2;
        uint32 orbitDefenseDestroyed2;
        uint40 arrivalTime;
        uint32 accumulatedDefenseAdded;
        uint32 accumulatedAttackAdded;
        uint16 attackPower;
        uint24 futureExtraProduction;
    }

    function _resolveFleet(uint256 fleetId, FleetResolution calldata resolution) internal {
        // -----------------------------------------------------------------------------------------------------------
        // Initialise State Update
        // -----------------------------------------------------------------------------------------------------------
        Planet storage toPlanet = _getPlanet(resolution.to);
        PlanetUpdateState memory toPlanetUpdate = _createPlanetUpdateState(toPlanet, resolution.to);
        ResolutionState memory rState = _createResolutionState(_fleets[fleetId], resolution.from);

        // -----------------------------------------------------------------------------------------------------------
        // check requirements
        // -----------------------------------------------------------------------------------------------------------

        require(
            rState.fleetQuantity > 0,
            rState.fleetOwner != address(0) ? "FLEET_RESOLVED_ALREADY" : "FLEET_DO_NOT_EXIST"
        );
        _requireCorrectDistance(
            resolution.distance,
            resolution.from,
            resolution.to,
            rState.fromData,
            toPlanetUpdate.data
        );
        _requireCorrectTimeAndUpdateArrivalTime(
            resolution.distance,
            resolution.arrivalTimeWanted,
            rState.fleetLaunchTime,
            rState.fromData,
            rState
        );

        if (_bootstrapSessionEndTime > 0) {
            uint256 timestamp = block.timestamp;

            if (timestamp >= _bootstrapSessionEndTime) {
                require(rState.fleetLaunchTime >= _infinityStartTime, "FLEET_LAUNCHED_IN_BOOTSTRAP");
            }
        }

        // -----------------------------------------------------------------------------------------------------------
        // Compute Basic Planet Updates
        // -----------------------------------------------------------------------------------------------------------
        _computePlanetUpdateForTimeElapsed(toPlanetUpdate);

        address ownerAtArrival = toPlanetUpdate.newOwner; // this can be owner == address(0)

        uint32 numSpaceshipsAtArrival = toPlanetUpdate.numSpaceships;

        // -----------------------------------------------------------------------------------------------------------
        // Traveling logic...
        // -----------------------------------------------------------------------------------------------------------

        _computeInFlightLossForFleet(rState, resolution);

        // -----------------------------------------------------------------------------------------------------------
        // Resolution logic...
        // -----------------------------------------------------------------------------------------------------------

        _updateFleetForGifting(rState, resolution, toPlanetUpdate.newOwner);

        _computeResolutionResult(rState, toPlanetUpdate);

        // -----------------------------------------------------------------------------------------------------------
        // Write New State
        // -----------------------------------------------------------------------------------------------------------

        _recordInOrbitLossAfterAttack(rState, toPlanetUpdate);

        _recordOrbitLossAccountingForFleetOrigin(rState, resolution);

        _setTravelingUpkeepFromOrigin(fleetId, rState, resolution.from);

        _setPlanet(toPlanet, toPlanetUpdate, rState.victory);

        _setAccumulatedAttack(rState, toPlanetUpdate);

        _fleets[fleetId].quantity = (1 << 31) | _fleets[fleetId].quantity;
        _fleets[fleetId].defender = ownerAtArrival;
        _fleets[fleetId].defenderLoss = rState.defenderLoss;
        _fleets[fleetId].arrivalTime = uint40(block.timestamp);
        _fleets[fleetId].planetActive = toPlanetUpdate.active;
        _fleets[fleetId].victory = rState.victory;

        // -----------------------------------------------------------------------------------------------------------
        // Events
        // -----------------------------------------------------------------------------------------------------------
        _emitFleetArrived(
            fleetId,
            rState,
            ownerAtArrival,
            resolution,
            _arrivalData(rState, toPlanetUpdate, numSpaceshipsAtArrival)
        );

        if (toPlanetUpdate.active && rState.victory) {
            // if active and the fleet was victorious we need to handle stake change of hands
            if (toPlanetUpdate.exitStartTime != 0) {
                // exit has been interupted
                // we add stake to new owner
                _notifyGeneratorAdd(toPlanetUpdate.newOwner, uint256(_stake(toPlanetUpdate.data)) * (DECIMALS_14));
            } else {
                // there was no exit, so we move the stake
                _notifyGeneratorMove(
                    toPlanetUpdate.owner,
                    toPlanetUpdate.newOwner,
                    uint256(_stake(toPlanetUpdate.data)) * (DECIMALS_14)
                );
            }
        }
    }

    function _arrivalData(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate,
        uint32 numSpaceshipsAtArrival
    ) internal pure returns (ArrivalData memory arrivalData) {
        arrivalData.newNumspaceships = toPlanetUpdate.numSpaceships;
        arrivalData.newTravelingUpkeep = toPlanetUpdate.travelingUpkeep;
        arrivalData.newOverflow = toPlanetUpdate.overflow;
        arrivalData.numSpaceshipsAtArrival = numSpaceshipsAtArrival;
        arrivalData.taxLoss = rState.taxed
            ? (rState.originalQuantity - rState.inFlightFleetLoss) - rState.fleetQuantity
            : 0;
        arrivalData.fleetLoss = rState.attackerLoss;
        arrivalData.planetLoss = rState.defenderLoss;
        arrivalData.inFlightFleetLoss = rState.inFlightFleetLoss;
        arrivalData.inFlightPlanetLoss = rState.inFlightPlanetLoss;
        arrivalData.accumulatedDefenseAdded = rState.accumulatedDefenseAdded;
        arrivalData.accumulatedAttackAdded = rState.accumulatedAttackAdded;
    }

    function _emitFleetArrived(
        uint256 fleetId,
        ResolutionState memory rState,
        address planetOwner,
        FleetResolution memory resolution,
        ArrivalData memory arrivalData
    ) internal {
        emit BlockTime(block.number, block.timestamp);
        emit FleetRevealed(
            fleetId,
            resolution.from,
            resolution.to,
            resolution.arrivalTimeWanted,
            resolution.gift,
            resolution.specific,
            resolution.secret,
            resolution.fleetSender,
            resolution.operator
        );
        emit FleetArrived(
            fleetId,
            rState.fleetOwner,
            planetOwner,
            resolution.to,
            rState.gifting,
            rState.victory,
            arrivalData
        );
    }

    function _requireCorrectDistance(
        uint256 distance,
        uint256 from,
        uint256 to,
        bytes32 fromPlanetData,
        bytes32 toPlanetData
    ) internal pure {
        // check input instead of compute sqrt

        (int8 fromSubX, int8 fromSubY) = _subLocation(fromPlanetData);
        (int8 toSubX, int8 toSubY) = _subLocation(toPlanetData);
        uint256 distanceSquared = uint256(
            int256( // check input instead of compute sqrt
                ((int128(int256(to & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) * 4 + toSubX) -
                    (int128(int256(from & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) * 4 + fromSubX)) **
                    2 +
                    ((int128(int256(to >> 128)) * 4 + toSubY) - (int128(int256(from >> 128)) * 4 + fromSubY)) ** 2
            )
        );
        require(distance ** 2 <= distanceSquared && distanceSquared < (distance + 1) ** 2, "wrong distance");
    }

    function _requireCorrectTimeAndUpdateArrivalTime(
        uint256 distance,
        uint256 arrivalTimeWanted,
        uint40 launchTime,
        bytes32 fromPlanetData,
        ResolutionState memory rState
    ) internal view {
        uint256 minReachTime = launchTime + (distance * (_timePerDistance * 10000)) / _speed(fromPlanetData);
        uint256 reachTime = Math.max(arrivalTimeWanted, minReachTime);
        if (arrivalTimeWanted > 0) {
            rState.arrivalTime = uint40(arrivalTimeWanted);
        } else {
            rState.arrivalTime = uint40(minReachTime);
        }
        require(block.timestamp >= reachTime, "too early");
        require(block.timestamp < reachTime + _resolveWindow, "too late, your spaceships are lost in space");
    }

    function _computeInFlightLossForFleet(
        ResolutionState memory rState,
        FleetResolution memory resolution
    ) internal view {
        // -----------------------------------------------------------------------------------------------------------
        // check if fleet was attacked while departing (used to prevent front-running, see fleet sending)
        // -----------------------------------------------------------------------------------------------------------
        uint256 timeSlot = rState.fleetLaunchTime / (_frontrunningDelay / 2);
        uint32 destroyed = _inFlight[resolution.from][timeSlot].destroyed;
        uint32 originalQuantity = rState.fleetQuantity;
        if (destroyed < rState.fleetQuantity) {
            rState.fleetQuantity -= uint32(destroyed);
        } else {
            rState.fleetQuantity = 0;
        }

        rState.inFlightFleetLoss = originalQuantity - rState.fleetQuantity;
        // -----------------------------------------------------------------------------------------------------------
    }

    function _updateFleetForGifting(
        ResolutionState memory rState,
        FleetResolution memory resolution,
        address destinationOwner
    ) internal view {
        (bool gifting, bool taxed) = _computeGifting(destinationOwner, resolution, rState);
        rState.gifting = gifting;
        rState.taxed = taxed;
    }

    // TODO simplify and apply that to attack (when fleetOwner is not fleetSender)
    //  if (resolution.gift) { rState.fleetOwner = destinationOwner }
    //  then compute tax based on fleetOwner != fleetSender, box for attacks and gift
    //  combined attack could even work for non-allies ?
    //  in _computeGift calculate the tax for every branch that result in `gifting` being false
    //  then in attack, add tax to the quantity of fleet + modify event

    // solhint-disable-next-line code-complexity
    function _computeGifting(
        address destinationOwner,
        FleetResolution memory resolution,
        ResolutionState memory rState
    ) internal view returns (bool gifting, bool taxed) {
        if (destinationOwner == address(0)) {
            // destination has no owner : this is an attack
            return (false, _isFleetOwnerTaxed(rState.fleetOwner, resolution.fleetSender, rState.fleetLaunchTime));
        }
        if (destinationOwner == rState.fleetOwner && destinationOwner == resolution.fleetSender) {
            // destination is sender is fleet owner: this is a non-taxed gift
            return (true, false);
        }

        if (resolution.gift || destinationOwner == rState.fleetOwner) {
            // intent was gift
            if (
                resolution.specific == address(0) || // anyone
                resolution.specific == destinationOwner || // only one address and matching owner
                destinationOwner == rState.fleetOwner // owner is fleet owner => gift
            ) {
                // and it was for anyone or specific destination owner that is the same as the current one
                // or it was simply that fleetOwner = destinationOwner

                // check tax applies with sender
                (, uint96 joinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                    resolution.fleetSender,
                    destinationOwner,
                    rState.fleetLaunchTime
                );
                return (true, joinTime == 0 || joinTime > rState.fleetLaunchTime);
            }

            if (resolution.specific == address(1)) {
                // or the specific specify any common alliances (1)

                if (rState.fleetOwner == resolution.fleetSender) {
                    (, uint96 joinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        resolution.fleetSender,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );
                    return (joinTime > 0, joinTime > rState.fleetLaunchTime);
                } else {
                    (, uint96 fleetOwnerJoinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        rState.fleetOwner,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );

                    if (fleetOwnerJoinTime == 0) {
                        // not in an alliance
                        return (
                            false,
                            _isFleetOwnerTaxed(rState.fleetOwner, resolution.fleetSender, rState.fleetLaunchTime)
                        );
                    }

                    // alliance => means gift
                    // check if taxed:
                    (, uint96 senderJoinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        resolution.fleetSender,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );

                    return (true, senderJoinTime == 0 || senderJoinTime > rState.fleetLaunchTime);
                }
            }

            if (uint160(resolution.specific) > 1) {
                // or a specific alliance that matches

                (uint96 joinTimeToSpecific, ) = _allianceRegistry.getAllianceData(
                    destinationOwner,
                    IAlliance(resolution.specific)
                );

                if (joinTimeToSpecific > 0) {
                    (, uint96 joinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        resolution.fleetSender,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );
                    return (true, joinTime == 0 || joinTime > rState.fleetLaunchTime);
                }
            }
        } else {
            // intent was attack
            if (resolution.specific == address(1)) {
                // and the attack was on any non-allies

                if (rState.fleetOwner == resolution.fleetSender) {
                    // make it a gift if the destination owner is actually an ally
                    (, uint96 joinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        resolution.fleetSender,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );
                    return (joinTime > 0, joinTime > rState.fleetLaunchTime);
                } else {
                    (, uint96 fleetOwnerJoinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        rState.fleetOwner,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );

                    if (fleetOwnerJoinTime == 0) {
                        // not in an alliance
                        return (
                            false,
                            _isFleetOwnerTaxed(rState.fleetOwner, resolution.fleetSender, rState.fleetLaunchTime)
                        );
                    }

                    // alliance => means gift
                    // check if taxed:
                    (, uint96 senderJoinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        resolution.fleetSender,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );

                    return (true, senderJoinTime == 0 || senderJoinTime > rState.fleetLaunchTime);
                }
            }

            if (uint160(resolution.specific) > 1 && resolution.specific != destinationOwner) {
                // but specific not matching current owner

                (uint96 joinTimeToSpecific, ) = _allianceRegistry.getAllianceData(
                    destinationOwner,
                    IAlliance(resolution.specific)
                );

                // make it a gift if the destination is not matching the specific alliance
                // (or owner, in which case since it is not an alliance, it will also not match)
                if (joinTimeToSpecific == 0) {
                    (, uint96 joinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(
                        resolution.fleetSender,
                        destinationOwner,
                        rState.fleetLaunchTime
                    );
                    return (true, joinTime == 0 || joinTime > rState.fleetLaunchTime);
                }
            }
        }
        return (false, _isFleetOwnerTaxed(rState.fleetOwner, resolution.fleetSender, rState.fleetLaunchTime));
    }

    function _isFleetOwnerTaxed(
        address fleetOwner,
        address fleetSender,
        uint40 fleetLaunchTime
    ) internal view returns (bool) {
        if (fleetOwner == fleetSender) {
            return false;
        }
        (, uint96 joinTime) = _allianceRegistry.havePlayersAnAllianceInCommon(fleetOwner, fleetSender, fleetLaunchTime);
        return joinTime == 0 || joinTime > fleetLaunchTime;
    }

    function _setTravelingUpkeepFromOrigin(uint256 fleetID, ResolutionState memory rState, uint256 location) internal {
        // // we have to update the origin
        Planet storage fromPlanet = _planets[location];
        PlanetUpdateState memory fromPlanetUpdate = _createPlanetUpdateState(fromPlanet, location);
        _computePlanetUpdateForTimeElapsed(fromPlanetUpdate);

        uint16 production = _production(fromPlanetUpdate.data);
        uint256 capWhenActive = _capWhenActive(production);

        uint256 refund = rState.futureExtraProduction;
        uint256 timePassed = block.timestamp - rState.fleetLaunchTime;
        uint256 amountProducedTheWholeTime = (timePassed * uint256(_productionSpeedUp) * uint256(production)) / 1 hours;
        uint256 consumed = amountProducedTheWholeTime +
            (amountProducedTheWholeTime * _upkeepProductionDecreaseRatePer10000th) /
            10000;
        if (consumed > refund) {
            refund = 0;
        } else {
            refund -= consumed;
        }

        int256 newTravelingUpkeep = int256(fromPlanetUpdate.travelingUpkeep) - int256(refund);
        if (newTravelingUpkeep < -int256(capWhenActive)) {
            newTravelingUpkeep = -int256(capWhenActive);
        }
        fromPlanetUpdate.travelingUpkeep = int40(newTravelingUpkeep);

        _setPlanet(fromPlanet, fromPlanetUpdate, false);

        emit BlockTime(block.number, block.timestamp);
        emit TravelingUpkeepRefund(
            location,
            fleetID,
            fromPlanetUpdate.numSpaceships,
            fromPlanetUpdate.travelingUpkeep,
            fromPlanetUpdate.overflow
        );
    }

    function _setAccumulatedAttack(ResolutionState memory rState, PlanetUpdateState memory toPlanetUpdate) internal {
        if (!rState.taxed) {
            AccumulatedAttack storage attack = _attacks[toPlanetUpdate.location][rState.fleetOwner][rState.arrivalTime];

            // NOTE: target is required for the case where a different player capture the planet in-between
            //  otherwise, that player would be hitted with higher attack than would be fair
            //  hmm would it acutally ? the accumulatedDefenseAdded would still be counted
            //  Indeed, the only real player affected by _attacks[location][fleetOwner][arrivalTime] is the fleetOwner
            //  regardless of who is owner of the planet
            // attack.target = toPlanetUpdate.owner;
            // we leave this as is as we do not want to change the struct
            attack.damageCausedSoFar = rState.defenderLoss + rState.inFlightPlanetLoss + rState.accumulatedDefenseAdded;
            attack.numAttackSpent =
                rState.attackerLoss +
                rState.accumulatedAttackAdded +
                // when victorius we consider the full number of spaceship as used
                // this way if a combined attack arrive later, it can still count the whole attack and get a refund
                (rState.victory ? toPlanetUpdate.numSpaceships : 0);
            attack.averageAttackPower = rState.attackPower;
        }
    }

    function _combinedRefund(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal view returns (uint256 accumulationRefund) {
        _updateAccumulation(rState, toPlanetUpdate);
        if (rState.accumulatedAttackAdded > 0) {
            uint16 attack = rState.attackPower;
            uint16 defense = _defense(toPlanetUpdate.data);
            uint256 numAttack = rState.fleetQuantity + rState.accumulatedAttackAdded;
            (uint32 attackerLoss, ) = _computeFight(numAttack, rState.accumulatedDefenseAdded, attack, defense);
            if (rState.accumulatedAttackAdded > attackerLoss) {
                accumulationRefund = rState.accumulatedAttackAdded - attackerLoss;
                if (accumulationRefund > rState.accumulatedAttackAdded) {
                    rState.accumulatedAttackAdded = 0;
                } else {
                    rState.accumulatedAttackAdded = uint32(uint256(rState.accumulatedAttackAdded) - accumulationRefund);
                }
            }
        }
    }

    function _createResolutionState(
        Fleet storage fleet,
        uint256 from
    ) internal view returns (ResolutionState memory rState) {
        uint32 q = fleet.quantity >> 31 == 1 ? 0 : fleet.quantity;
        rState.fleetOwner = fleet.owner;
        rState.fleetLaunchTime = fleet.launchTime;
        rState.originalQuantity = q;
        rState.fleetQuantity = q;
        rState.futureExtraProduction = fleet.futureExtraProduction;
        rState.fromData = _planetData(from);
        rState.attackPower = _attack(rState.fromData);
    }

    function _recordOrbitLossAccountingForFleetOrigin(
        ResolutionState memory rState,
        FleetResolution memory resolution
    ) internal {
        if (rState.inFlightFleetLoss > 0) {
            uint256 timeSlot = rState.fleetLaunchTime / (_frontrunningDelay / 2);

            // NOTE we already computed that destroyed cannot be smaller than inFlightFleetLoss
            //  see _computeInFlightLossForFleet
            _inFlight[resolution.from][timeSlot].destroyed -= rState.inFlightFleetLoss;
        }
    }

    function _computeResolutionResult(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal view {
        if (rState.taxed) {
            rState.fleetQuantity = uint32(
                uint256(rState.fleetQuantity) - (uint256(rState.fleetQuantity) * _giftTaxPer10000) / 10000
            );
        }
        if (rState.gifting) {
            _computeGiftingResolutionResult(rState, toPlanetUpdate);
        } else {
            _computeAttackResolutionResult(rState, toPlanetUpdate);
        }
    }

    function _computeGiftingResolutionResult(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal view {
        uint256 newNumSpaceships = toPlanetUpdate.numSpaceships +
            rState.fleetQuantity +
            _combinedRefund(rState, toPlanetUpdate);
        if (newNumSpaceships >= ACTIVE_MASK) {
            newNumSpaceships = ACTIVE_MASK - 1;
        }

        toPlanetUpdate.numSpaceships = uint32(newNumSpaceships);
        if (!toPlanetUpdate.active) {
            // NOTE: not active, overflow is applied on cap = 0
            if (toPlanetUpdate.numSpaceships > toPlanetUpdate.overflow) {
                toPlanetUpdate.overflow = toPlanetUpdate.numSpaceships;
            }
        } else {
            uint32 cap = uint32(_capWhenActive(_production(toPlanetUpdate.data)));
            if (_productionCapAsDuration > 0 && newNumSpaceships > cap) {
                if (toPlanetUpdate.numSpaceships - cap > toPlanetUpdate.overflow) {
                    toPlanetUpdate.overflow = uint32(toPlanetUpdate.numSpaceships - cap);
                }
            } else {
                toPlanetUpdate.overflow = 0;
            }
        }
    }

    function _updateAccumulation(ResolutionState memory rState, PlanetUpdateState memory toPlanetUpdate) internal view {
        // TODO 45min config ?
        if (!rState.taxed && block.timestamp < rState.arrivalTime + 45 minutes) {
            AccumulatedAttack memory acc = _attacks[toPlanetUpdate.location][rState.fleetOwner][rState.arrivalTime];

            // TODO  acc.target == toPlanetUpdate.owner || toPlanetUpdate.owner == fleetOwner  so your combined attack works when you get it
            // what about your allies ?
            // taxed work as he accumulated attack is already shared with allies (s)
            // so we should not need to modify here ?
            // if (acc.target == toPlanetUpdate.owner && acc.numAttackSpent != 0) {
            if (acc.numAttackSpent != 0) {
                rState.attackPower = uint16(
                    (uint256(rState.attackPower) *
                        uint256(rState.fleetQuantity) +
                        uint256(acc.averageAttackPower) *
                        uint256(acc.numAttackSpent)) / (uint256(rState.fleetQuantity) + uint256(acc.numAttackSpent))
                );
                rState.accumulatedAttackAdded = acc.numAttackSpent;
                rState.accumulatedDefenseAdded = acc.damageCausedSoFar;
            }
        }
    }

    function _computeAttackResolutionResult(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal view {
        // NOTE natives come back to power once numSPaceships == 0 and planet not active
        if (!toPlanetUpdate.active && toPlanetUpdate.numSpaceships < _natives(toPlanetUpdate.data)) {
            _updatePlanetUpdateStateAndResolutionStateForNativeAttack(rState, toPlanetUpdate);
        } else {
            _updateAccumulation(rState, toPlanetUpdate);

            _updatePlanetUpdateStateAndResolutionStateForPlanetAttack(rState, toPlanetUpdate);
        }
    }

    function _updatePlanetUpdateStateAndResolutionStateForNativeAttack(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal view {
        // NOTE: when we are dealing with native attacks, we do not consider combined attacks
        // TODO We need to consider that case in the UI
        uint16 attack = _attack(rState.fromData);
        uint16 defense = _defense(toPlanetUpdate.data);
        uint16 natives = _natives(toPlanetUpdate.data);
        (uint32 attackerLoss, uint32 defenderLoss) = _computeFight(rState.fleetQuantity, natives, attack, defense);
        rState.attackerLoss = attackerLoss;
        if (defenderLoss == natives && rState.fleetQuantity > attackerLoss) {
            // (attackerLoss: 0, defenderLoss: 0) means that numAttack was zero as natives cannot be zero
            toPlanetUpdate.numSpaceships = rState.fleetQuantity - attackerLoss;
            rState.defenderLoss = defenderLoss;
            rState.victory = true;
            toPlanetUpdate.newOwner = rState.fleetOwner;
            // solhint-disable-next-line no-empty-blocks
        }
        // NOTE else (attacker lost) then nothing happen
    }

    function _updatePlanetUpdateStateAndResolutionStateForPlanetAttack(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal view {
        _updateResolutionStateFromOrbitDefense(rState, toPlanetUpdate);
        uint256 numDefense = toPlanetUpdate.numSpaceships +
            rState.accumulatedDefenseAdded +
            rState.orbitDefense1 +
            rState.orbitDefense2;
        uint16 production = _production(toPlanetUpdate.data);

        if (numDefense == 0 && rState.fleetQuantity > 0) {
            // scenario where there is actually no defense on the place,

            toPlanetUpdate.newOwner = rState.fleetOwner;
            toPlanetUpdate.numSpaceships = rState.fleetQuantity;
            if (!toPlanetUpdate.active) {
                // numDefense = 0 so numAttack is the overflow, attacker took over
                toPlanetUpdate.overflow = toPlanetUpdate.numSpaceships;
            } else {
                if (_productionCapAsDuration > 0) {
                    uint32 cap = uint32(_capWhenActive(production));
                    if (toPlanetUpdate.numSpaceships > cap) {
                        // numDefense = 0 so numAttack is the overflow, attacker took over
                        toPlanetUpdate.overflow = uint32(toPlanetUpdate.numSpaceships - cap);
                    } else {
                        toPlanetUpdate.overflow = 0;
                    }
                }
            }

            rState.victory = true;
        } else {
            _computeAttack(rState, toPlanetUpdate, numDefense);
            _computeTravelingUpkeepReductionFromDefenseLoss(rState, toPlanetUpdate, production);
        }
    }

    function _updateResolutionStateFromOrbitDefense(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal view {
        // -----------------------------------------------------------------------------------------------------------
        // consider fleets that just departed from the planet (used to prevent front-running, see fleet sending)
        // -----------------------------------------------------------------------------------------------------------
        uint256 timeSlot = block.timestamp / (_frontrunningDelay / 2);
        InFlight storage slot1 = _inFlight[toPlanetUpdate.location][timeSlot - 1];
        rState.orbitDefense1 = slot1.flying > 2 ** 31 ? 2 ** 31 - 1 : uint32(slot1.flying);
        rState.orbitDefenseDestroyed1 = slot1.destroyed > 2 ** 31 ? 2 ** 31 - 1 : uint32(slot1.destroyed);
        InFlight storage slot2 = _inFlight[toPlanetUpdate.location][timeSlot];
        rState.orbitDefense2 = slot2.flying > 2 ** 31 ? 2 ** 31 - 1 : uint32(slot2.flying);
        rState.orbitDefenseDestroyed2 = slot2.destroyed > 2 ** 31 ? 2 ** 31 - 1 : uint32(slot2.destroyed);
    }

    // solhint-disable-next-line code-complexity
    function _computeAttack(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate,
        uint256 numDefense
    ) internal view {
        uint16 attack = rState.attackPower;
        uint16 defense = _defense(toPlanetUpdate.data);
        uint256 numAttack = rState.fleetQuantity + rState.accumulatedAttackAdded;
        (uint32 attackerLoss, uint32 defenderLoss) = _computeFight(numAttack, numDefense, attack, defense);
        rState.defenderLoss = defenderLoss;
        rState.attackerLoss = rState.accumulatedAttackAdded > attackerLoss
            ? 0
            : attackerLoss - rState.accumulatedAttackAdded;

        // (attackerLoss: 0, defenderLoss: 0) could either mean attack was zero or defense was zero :
        if (rState.fleetQuantity > 0 && rState.defenderLoss == numDefense) {
            // NOTE Attacker wins

            // all orbiting fleets are destroyed, inFlightPlanetLoss is all that is left
            uint256 inFlightPlanetLoss = numDefense - toPlanetUpdate.numSpaceships - rState.accumulatedDefenseAdded;
            if (inFlightPlanetLoss > ACTIVE_MASK) {
                // cap it
                // TODO investigate potential issues
                inFlightPlanetLoss = ACTIVE_MASK - 1;
            }
            rState.inFlightPlanetLoss = uint32(inFlightPlanetLoss);

            rState.defenderLoss = rState.defenderLoss - rState.inFlightPlanetLoss;

            toPlanetUpdate.numSpaceships = rState.fleetQuantity - rState.attackerLoss;
            rState.victory = true;

            toPlanetUpdate.newOwner = rState.fleetOwner;

            if (!toPlanetUpdate.active) {
                // attack took over, overflow is numSpaceships
                toPlanetUpdate.overflow = toPlanetUpdate.numSpaceships;
            } else {
                if (_productionCapAsDuration > 0) {
                    uint16 production = _production(toPlanetUpdate.data);
                    uint32 cap = uint32(_capWhenActive(production));
                    if (toPlanetUpdate.numSpaceships > cap) {
                        if (toPlanetUpdate.numSpaceships - cap > toPlanetUpdate.overflow) {
                            toPlanetUpdate.overflow = toPlanetUpdate.numSpaceships - cap;
                        }
                    } else {
                        toPlanetUpdate.overflow = 0;
                    }
                }
            }
        } else if (rState.attackerLoss == rState.fleetQuantity) {
            // NOTE Defender wins

            if (defenderLoss > toPlanetUpdate.numSpaceships + rState.accumulatedDefenseAdded) {
                rState.inFlightPlanetLoss =
                    defenderLoss -
                    toPlanetUpdate.numSpaceships -
                    rState.accumulatedDefenseAdded;

                toPlanetUpdate.numSpaceships = 0;
                // TODO change owner already if incative ?
                //  not needed though as this is the same has having numSpaceships = 1 and become zero over time

                if (rState.orbitDefense1 >= rState.inFlightPlanetLoss) {
                    rState.orbitDefense1 -= rState.inFlightPlanetLoss;
                    rState.orbitDefenseDestroyed1 += rState.inFlightPlanetLoss;
                } else {
                    rState.orbitDefenseDestroyed1 += rState.orbitDefense1;
                    uint32 extra = (rState.inFlightPlanetLoss - rState.orbitDefense1);
                    if (rState.orbitDefense2 >= extra) {
                        rState.orbitDefense2 -= extra;
                        rState.orbitDefenseDestroyed2 += extra;
                    } else {
                        rState.orbitDefenseDestroyed2 += rState.orbitDefense2;
                        rState.orbitDefense2 = 0; // should never reach minus but let simply set it to zero
                    }
                    rState.orbitDefense1 = 0;
                }
            } else {
                toPlanetUpdate.numSpaceships =
                    toPlanetUpdate.numSpaceships +
                    rState.accumulatedDefenseAdded -
                    defenderLoss;

                // TODO change owner already if incative and numSpaceship == 0 (like above)
                //  not needed though as this is the same has having numSpaceships = 1 and become zero over time
            }

            // same as numSpaceshipAtArrival - toPlanetUpdate.numSpaceship;
            rState.defenderLoss = rState.defenderLoss - rState.inFlightPlanetLoss - rState.accumulatedDefenseAdded;

            if (!toPlanetUpdate.active) {
                if (defenderLoss > toPlanetUpdate.overflow) {
                    toPlanetUpdate.overflow = 0;
                } else {
                    toPlanetUpdate.overflow -= defenderLoss;
                }
            } else {
                if (_productionCapAsDuration > 0) {
                    uint16 production = _production(toPlanetUpdate.data);
                    uint32 cap = uint32(_capWhenActive(production));
                    if (toPlanetUpdate.numSpaceships > cap) {
                        if (defenderLoss <= toPlanetUpdate.overflow) {
                            toPlanetUpdate.overflow -= defenderLoss;
                        } else {
                            toPlanetUpdate.overflow = 0;
                        }
                    } else {
                        toPlanetUpdate.overflow = 0;
                    }
                }
            }
        } else {
            // should not happen
            // because we check for numDefense == 0 before performing the attack, see _updatePlanetUpdateStateAndResolutionStateForPlanetAttack
            revert("ZERO_ZERO");
        }
    }

    function _computeFight(
        uint256 numAttack,
        uint256 numDefense,
        uint256 attack,
        uint256 defense
    ) internal view returns (uint32 attackerLoss, uint32 defenderLoss) {
        if (numAttack == 0 || numDefense == 0) {
            // this edge case need to be considered,
            // as the result of this function cannot tell from it whos is winning here
            return (0, 0);
        }

        uint256 attackFactor = numAttack *
            ((1000000 - _fleetSizeFactor6) + ((_fleetSizeFactor6 * numAttack) / numDefense));
        uint256 attackDamage = (attackFactor * attack) / defense / 1000000;

        if (numDefense > attackDamage) {
            // attack fails
            attackerLoss = uint32(numAttack); // all attack destroyed
            defenderLoss = uint32(attackDamage); // 1 spaceship will be left at least as attackDamage < numDefense
        } else {
            // attack succeed
            uint256 defenseFactor = numDefense *
                ((1000000 - _fleetSizeFactor6) + ((_fleetSizeFactor6 * numDefense) / numAttack));
            uint256 defenseDamage = uint32((defenseFactor * defense) / attack / 1000000);

            if (defenseDamage >= numAttack) {
                defenseDamage = numAttack - 1; // ensure 1 spaceship left
            }

            attackerLoss = uint32(defenseDamage);
            defenderLoss = uint32(numDefense); // all defense destroyed
        }
    }

    function _computeTravelingUpkeepReductionFromDefenseLoss(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate,
        uint16 production
    ) internal view {
        // allow the attacker to pay for upkeep as part of the attack
        // only get to keep the upkeep that was there as a result of spaceships sent away

        uint256 capWhenActive = _capWhenActive(production);

        int256 totalDefenseLoss = int256(uint256(rState.defenderLoss) + uint256(rState.inFlightPlanetLoss));
        int256 newTravelingUpkeep = int256(toPlanetUpdate.travelingUpkeep) - totalDefenseLoss;
        if (newTravelingUpkeep < -int256(capWhenActive)) {
            newTravelingUpkeep = -int256(capWhenActive);
        }
        toPlanetUpdate.travelingUpkeep = int40(newTravelingUpkeep);
    }

    function _recordInOrbitLossAfterAttack(
        ResolutionState memory rState,
        PlanetUpdateState memory toPlanetUpdate
    ) internal {
        if (rState.inFlightPlanetLoss > 0) {
            InFlight storage slot1 = _inFlight[toPlanetUpdate.location][block.timestamp / (_frontrunningDelay / 2) - 1];
            slot1.flying = rState.orbitDefense1;
            slot1.destroyed = rState.orbitDefenseDestroyed1;

            InFlight storage slot2 = _inFlight[toPlanetUpdate.location][block.timestamp / (_frontrunningDelay / 2)];
            slot2.flying = rState.orbitDefense2;
            slot2.destroyed = rState.orbitDefenseDestroyed2;
        }
    }

    function _callWithGas(address to, bytes memory data, uint256 gas) internal {
        // We want to ensure enough gas were given for the generator, but no more
        // This way if the generator is broken/compromised (we are planning to update it)
        // then this will always continue to work
        // Reversely, a player have to provide enough gas
        // and we want to ensure the player can't force a revert on the hook
        // In particular. to prevent players to make a call to `remove` fails

        if (to != address(0)) {
            // we could do the check prior:
            // uint256 gasAvailable = gasleft() - 2000;
            // require(gasAvailable - gasAvailable / 64  >= gas, "NOT_ENOUGH_GAS_FOR_INNER_CALL");
            // to.call{gas: gas}(data);
            // but we instead chose to do the check after.
            // for more info see: https://ronan.eth.limo/blog/ethereum-gas-dangers/

            to.call{gas: gas}(data);
            // we use after the gas check as this allow us to not require heavy gas use if not needed
            // instead of + 100,000 for 96,000 gas we can just add 1,524 gas (+ a bit more)
            require(gasleft() > gas / 63, "NOT_ENOUGH_GAS_FOR_INNER_CALL");
        }
    }

    function _generator() internal view returns (address generator) {
        assembly {
            // keccak256("generator") - 1
            generator := sload(0x27ec6af4a6510eb9b7e0cc7f39415b7f15e430e53eb0cd3997e7c7e0cf680f6e)
        }
    }

    function _notifyGeneratorAdd(address player, uint256 amount) internal {
        _callWithGas(_generator(), abi.encodeWithSelector(IOnStakeChange.add.selector, player, amount), 96000);
    }

    function _notifyGeneratorRemove(address player, uint256 amount) internal {
        _callWithGas(_generator(), abi.encodeWithSelector(IOnStakeChange.remove.selector, player, amount), 96000);
    }

    function _notifyGeneratorMove(address from, address to, uint256 amount) internal {
        _callWithGas(_generator(), abi.encodeWithSelector(IOnStakeChange.move.selector, from, to, amount), 192000);
    }

    // ---------------------------------------------------------------------------------------------------------------
    // PLANET STATS
    // ---------------------------------------------------------------------------------------------------------------

    function _planetData(uint256 location) internal view returns (bytes32) {
        return keccak256(abi.encodePacked(_genesis, location));
    }

    function _subLocation(bytes32 data) internal pure returns (int8 subX, int8 subY) {
        subX = 1 - int8(data.value8Mod(0, 3));
        subY = 1 - int8(data.value8Mod(2, 3));
    }

    function _stake(bytes32 data) internal view returns (uint32) {
        require(_exists(data), "PLANET_NOT_EXISTS");
        // return data.normal16(4, 0x000400050005000A000A000F000F00140014001E001E00280028005000500064);
        uint8 productionIndex = data.normal8(12); // production affect the stake value

        // TODO remove or decide otherwise:
        // uint16 offset = data.normal16(4, 0x0000000100010002000200030003000400040005000500060006000700070008);
        // uint16 stakeIndex = productionIndex + offset;
        // if (stakeIndex < 4) {
        //     stakeIndex = 0;
        // } else if (stakeIndex > 19) {
        //     stakeIndex = 15;
        // } else {
        //     stakeIndex -= 4;
        // }
        uint16 stakeIndex = productionIndex;
        return
            uint32(
                uint256(
                    uint16(uint8(_stakeRange[stakeIndex * 2])) * 0x100 + uint16(uint8(_stakeRange[stakeIndex * 2 + 1]))
                ) * _stakeMultiplier10000th
            );
    }

    function _production(bytes32 data) internal pure returns (uint16) {
        require(_exists(data), "PLANET_NOT_EXISTS");
        // TODO TRY : 1800,2100,2400,2700,3000,3300,3600, 3600, 3600, 3600,4000,4400,4800,5400,6200,7200 ?

        // 1800,2100,2400,2700,3000,3300,3600, 3600, 3600, 3600,4200,5400,6600,7800,9000,12000
        // 0x0708083409600a8c0bb80ce40e100e100e100e101068151819c81e7823282ee0
        return data.normal16(12, 0x0708083409600a8c0bb80ce40e100e100e100e101068151819c81e7823282ee0); // per hour
    }

    function _capWhenActive(uint16 production) internal view returns (uint256) {
        return _acquireNumSpaceships + (uint256(production) * _productionCapAsDuration) / 1 hours;
    }

    function _attack(bytes32 data) internal pure returns (uint16) {
        require(_exists(data), "PLANET_NOT_EXISTS");
        return 4000 + data.normal8(20) * 400; // 4,000 - 7,000 - 10,000
    }

    function _defense(bytes32 data) internal pure returns (uint16) {
        require(_exists(data), "PLANET_NOT_EXISTS");
        return 4000 + data.normal8(28) * 400; // 4,000 - 7,000 - 10,000
    }

    function _speed(bytes32 data) internal pure returns (uint16) {
        require(_exists(data), "PLANET_NOT_EXISTS");
        return 5005 + data.normal8(36) * 333; // 5,005 - 7,502.5 - 10,000
    }

    function _natives(bytes32 data) internal pure returns (uint16) {
        require(_exists(data), "PLANET_NOT_EXISTS");
        return 15000 + data.normal8(44) * 3000; // 15,000 - 37,500 - 60,000
    }

    function _exists(bytes32 data) internal pure returns (bool) {
        return data.value8Mod(52, 16) == 1; // 16 => 36 so : 1 planet per 6 (=24 min unit) square
        // also:
        // 20000 average starting numSpaceships (or max?)
        // speed of min unit = 30 min ( 1 hour per square)
        // production : 20000 per 6 hours
        // exit : 3 days ? => 72 distance
    }

    // ---------------------------------------------------------------------------------------------------------------
    // GETTERS
    // ---------------------------------------------------------------------------------------------------------------

    function _getPlanet(uint256 location) internal view returns (Planet storage) {
        return _planets[location];
    }

    function _getPlanetStats(uint256 location) internal view returns (PlanetStats memory) {
        bytes32 data = _planetData(location);
        require(_exists(data), "no planet in this location");

        (int8 subX, int8 subY) = _subLocation(data);
        return
            PlanetStats({
                subX: subX,
                subY: subY,
                stake: _stake(data),
                production: _production(data),
                attack: _attack(data),
                defense: _defense(data),
                speed: _speed(data),
                natives: _natives(data)
            });
    }

    // ---------------------------------------------------------------------------------------------------------------
    // UTILS
    // ---------------------------------------------------------------------------------------------------------------

    function _activeNumSpaceships(uint32 numSpaceshipsData) internal pure returns (bool active, uint32 numSpaceships) {
        active = (numSpaceshipsData & ACTIVE_MASK) == ACTIVE_MASK;
        numSpaceships = numSpaceshipsData % (ACTIVE_MASK);
    }

    function _setActiveNumSpaceships(bool active, uint32 numSpaceships) internal pure returns (uint32) {
        return uint32((active ? ACTIVE_MASK : 0) + numSpaceships);
    }

    function _msgSender() internal view returns (address) {
        return msg.sender; // TODO metatx
    }

    modifier whenNotPaused() {
        if (_bootstrapSessionEndTime > 0) {
            uint256 timestamp = block.timestamp;
            uint256 pauseStart = _bootstrapSessionEndTime;
            uint256 pauseEnd = _infinityStartTime;

            require(timestamp < pauseStart || timestamp >= pauseEnd, "PAUSED");
        }
        _;
    }
}

File 16 of 18 : IOnStakeChange.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

interface IOnStakeChange {
    function add(address account, uint256 amount) external;

    function remove(address account, uint256 amount) external;

    function move(
        address from,
        address to,
        uint256 amount
    ) external;
}

File 17 of 18 : IOuterSpaceFleetsRead.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

import "../types/ImportingOuterSpaceTypes.sol";
import "../events/ImportingOuterSpaceEvents.sol";

interface IOuterSpaceFleetsRead is ImportingOuterSpaceTypes, ImportingOuterSpaceEvents {
    function getFleet(
        uint256 fleetId,
        uint256 from
    )
        external
        view
        returns (
            address owner,
            uint40 launchTime,
            uint32 quantity,
            uint64 flyingAtLaunch, // can be more than quantity if multiple fleet were launched around the same time from the same planet
            uint64 destroyedAtLaunch
        );

    function getFleetData(uint256 fleetId, uint256 from) external view returns (FleetData memory data);
}

File 18 of 18 : ImportingOuterSpaceTypes.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.9;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IOnStakeChange.sol";

interface ImportingOuterSpaceTypes {
    // front running protection : _frontruunningDelay / 2 slots
    struct InFlight {
        uint32 flying;
        uint32 destroyed;
        // STORE last attack too, to compute combined attack on it ? uint128 is plainty enough
    }

    // TODO remove
    // struct Account {
    //     // TODO add more info
    //     // stake for example ? => coild it be used by staking ?
    //     // numPlanets ?
    //     // numSpaceships ? => probably too much ?
    //     uint64 totalProduction;
    //     uint64 productionDebt;
    // }

    struct Discovered {
        uint32 minX;
        uint32 maxX;
        uint32 minY;
        uint32 maxY;
    }

    // TODO split in 2 structs ? PlanetOwnership and PlanetState ?
    struct Planet {
        address owner;
        uint40 ownershipStartTime; // ~ 34865 years, should be enough :)
        uint40 exitStartTime; // ~ 34865 years, should be enough :)
        // TODO uint16 ?
        ///
        uint32 numSpaceships; // uint31 + first bit => active // TODO use bool active ?
        uint40 lastUpdated; // ~ 34865 years, should be enough :)
        int40 travelingUpkeep; // decrease per _upkeepProductionDecreaseRatePer10000th  * production
        uint32 overflow;
        // bool active; // TODO ?
        // bool exiting; // TODO ?
    }

    struct Fleet {
        address owner;
        uint40 launchTime; // ~ 34865 years, should be enough :)
        uint32 quantity; // TODO? first bit = done? to keep quantity value on-chain post resolution, actually not needed, can be given in the hash
        uint24 futureExtraProduction;
        address defender;
        uint40 arrivalTime;
        uint32 defenderLoss;
        bool planetActive;
        bool victory;
        // we got 24bit more to store if needed
        // operator ? // signer ?
    }

    struct FleetData {
        bool arrived;
        address owner;
        uint40 launchTime;
        uint32 quantity;
        uint64 flyingAtLaunch; // can be more than quantity if multiple fleet were launched around the same time from the same planet
        uint64 destroyedAtLaunch;
        address defender;
        uint40 arrivalTime;
        uint32 defenderLoss;
        bool planetActive;
        bool victory;
    }

    struct PlanetStats {
        int8 subX;
        int8 subY;
        uint32 stake;
        uint16 production;
        uint16 attack;
        uint16 defense;
        uint16 speed;
        uint16 natives;
    }

    struct ExternalPlanet {
        address owner;
        uint40 ownershipStartTime; // ~ 34865 years, should be enough :)
        uint40 exitStartTime; // ~ 34865 years, should be enough :)
        uint32 numSpaceships;
        uint32 overflow;
        uint40 lastUpdated; // ~ 34865 years, should be enough :)
        bool active;
        // bool exiting;
        uint256 reward;
    }

    struct FleetLaunch {
        address fleetSender;
        address fleetOwner;
        uint256 from;
        uint32 quantity;
        bytes32 toHash;
    }
    struct FleetResolution {
        uint256 from;
        uint256 to;
        uint256 distance;
        uint256 arrivalTimeWanted;
        bool gift;
        address specific;
        bytes32 secret;
        address fleetSender; // does not work ?
        address operator; // should be saved ?
    }

    struct AccumulatedAttack {
        address target;
        uint32 numAttackSpent;
        uint32 damageCausedSoFar;
        uint16 averageAttackPower;
    }
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"components":[{"internalType":"contract StakingToken","name":"stakingToken","type":"address"},{"internalType":"contract IFreePlayToken","name":"freeStakingToken","type":"address"},{"internalType":"contract AllianceRegistry","name":"allianceRegistry","type":"address"},{"internalType":"bytes32","name":"genesis","type":"bytes32"},{"internalType":"uint256","name":"resolveWindow","type":"uint256"},{"internalType":"uint256","name":"timePerDistance","type":"uint256"},{"internalType":"uint256","name":"exitDuration","type":"uint256"},{"internalType":"uint32","name":"acquireNumSpaceships","type":"uint32"},{"internalType":"uint32","name":"productionSpeedUp","type":"uint32"},{"internalType":"uint256","name":"frontrunningDelay","type":"uint256"},{"internalType":"uint256","name":"productionCapAsDuration","type":"uint256"},{"internalType":"uint256","name":"upkeepProductionDecreaseRatePer10000th","type":"uint256"},{"internalType":"uint256","name":"fleetSizeFactor6","type":"uint256"},{"internalType":"uint32","name":"initialSpaceExpansion","type":"uint32"},{"internalType":"uint32","name":"expansionDelta","type":"uint32"},{"internalType":"uint256","name":"giftTaxPer10000","type":"uint256"},{"internalType":"bytes32","name":"stakeRange","type":"bytes32"},{"internalType":"uint256","name":"stakeMultiplier10000th","type":"uint256"},{"internalType":"uint256","name":"bootstrapSessionEndTime","type":"uint256"},{"internalType":"uint256","name":"infinityStartTime","type":"uint256"}],"internalType":"struct OuterSpaceFacetBase.Config","name":"config","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"block","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"BlockTime","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stake","type":"uint256"}],"name":"ExitComplete","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fleet","type":"uint256"},{"indexed":true,"internalType":"address","name":"fleetOwner","type":"address"},{"indexed":true,"internalType":"address","name":"destinationOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"destination","type":"uint256"},{"indexed":false,"internalType":"bool","name":"gift","type":"bool"},{"indexed":false,"internalType":"bool","name":"won","type":"bool"},{"components":[{"internalType":"uint32","name":"newNumspaceships","type":"uint32"},{"internalType":"int40","name":"newTravelingUpkeep","type":"int40"},{"internalType":"uint32","name":"newOverflow","type":"uint32"},{"internalType":"uint32","name":"numSpaceshipsAtArrival","type":"uint32"},{"internalType":"uint32","name":"taxLoss","type":"uint32"},{"internalType":"uint32","name":"fleetLoss","type":"uint32"},{"internalType":"uint32","name":"planetLoss","type":"uint32"},{"internalType":"uint32","name":"inFlightFleetLoss","type":"uint32"},{"internalType":"uint32","name":"inFlightPlanetLoss","type":"uint32"},{"internalType":"uint32","name":"accumulatedDefenseAdded","type":"uint32"},{"internalType":"uint32","name":"accumulatedAttackAdded","type":"uint32"}],"indexed":false,"internalType":"struct ImportingOuterSpaceEvents.ArrivalData","name":"data","type":"tuple"}],"name":"FleetArrived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fleetId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"from","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"to","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"arrivalTimeWanted","type":"uint256"},{"indexed":false,"internalType":"bool","name":"gift","type":"bool"},{"indexed":false,"internalType":"address","name":"specific","type":"address"},{"indexed":false,"internalType":"bytes32","name":"secret","type":"bytes32"},{"indexed":false,"internalType":"address","name":"fleetSender","type":"address"},{"indexed":false,"internalType":"address","name":"operator","type":"address"}],"name":"FleetRevealed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fleetSender","type":"address"},{"indexed":true,"internalType":"address","name":"fleetOwner","type":"address"},{"indexed":true,"internalType":"uint256","name":"from","type":"uint256"},{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"fleet","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"quantity","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"newNumSpaceships","type":"uint32"},{"indexed":false,"internalType":"int40","name":"newTravelingUpkeep","type":"int40"},{"indexed":false,"internalType":"uint32","name":"newOverflow","type":"uint32"}],"name":"FleetSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newGeneratorAdmin","type":"address"}],"name":"GeneratorAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newGenerator","type":"address"}],"name":"GeneratorChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"genesis","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"resolveWindow","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timePerDistance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"exitDuration","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"acquireNumSpaceships","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"productionSpeedUp","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"frontrunningDelay","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"productionCapAsDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"upkeepProductionDecreaseRatePer10000th","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fleetSizeFactor6","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"initialSpaceExpansion","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"expansionDelta","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"giftTaxPer10000","type":"uint256"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"}],"name":"PlanetExit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"}],"name":"PlanetReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"acquirer","type":"address"},{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"numSpaceships","type":"uint32"},{"indexed":false,"internalType":"int40","name":"travelingUpkeep","type":"int40"},{"indexed":false,"internalType":"uint32","name":"overflow","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"stake","type":"uint256"},{"indexed":false,"internalType":"bool","name":"freegift","type":"bool"}],"name":"PlanetStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"},{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"newNumspaceships","type":"uint32"},{"indexed":false,"internalType":"int40","name":"newTravelingUpkeep","type":"int40"},{"indexed":false,"internalType":"uint32","name":"newOverflow","type":"uint32"}],"name":"PlanetTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"},{"indexed":true,"internalType":"address","name":"giver","type":"address"},{"indexed":false,"internalType":"uint256","name":"rewardId","type":"uint256"}],"name":"RewardSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"rewardId","type":"uint256"}],"name":"RewardToWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"newStake","type":"uint256"},{"indexed":false,"internalType":"bool","name":"freegift","type":"bool"}],"name":"StakeToWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"location","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"origin","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"fleet","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"newNumspaceships","type":"uint32"},{"indexed":false,"internalType":"int40","name":"newTravelingUpkeep","type":"int40"},{"indexed":false,"internalType":"uint32","name":"newOverflow","type":"uint32"}],"name":"TravelingUpkeepRefund","type":"event"},{"inputs":[{"internalType":"uint256","name":"fleetId","type":"uint256"},{"internalType":"uint256","name":"from","type":"uint256"}],"name":"getFleet","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint40","name":"launchTime","type":"uint40"},{"internalType":"uint32","name":"quantity","type":"uint32"},{"internalType":"uint64","name":"flyingAtLaunch","type":"uint64"},{"internalType":"uint64","name":"destroyedAtLaunch","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fleetId","type":"uint256"},{"internalType":"uint256","name":"from","type":"uint256"}],"name":"getFleetData","outputs":[{"components":[{"internalType":"bool","name":"arrived","type":"bool"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint40","name":"launchTime","type":"uint40"},{"internalType":"uint32","name":"quantity","type":"uint32"},{"internalType":"uint64","name":"flyingAtLaunch","type":"uint64"},{"internalType":"uint64","name":"destroyedAtLaunch","type":"uint64"},{"internalType":"address","name":"defender","type":"address"},{"internalType":"uint40","name":"arrivalTime","type":"uint40"},{"internalType":"uint32","name":"defenderLoss","type":"uint32"},{"internalType":"bool","name":"planetActive","type":"bool"},{"internalType":"bool","name":"victory","type":"bool"}],"internalType":"struct ImportingOuterSpaceTypes.FleetData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100365760003560e01c80637a7f1e421461003b578063ee54339314610064575b600080fd5b61004e610049366004610522565b6100d1565b60405161005b9190610544565b60405180910390f35b610077610072366004610522565b61036e565b6040805173ffffffffffffffffffffffffffffffffffffffff909616865264ffffffffff909416602086015263ffffffff9092169284019290925267ffffffffffffffff918216606084015216608082015260a00161005b565b6040805161016081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101919091526000838152600160208181526040808420815161012081018352815473ffffffffffffffffffffffffffffffffffffffff808216835264ffffffffff7401000000000000000000000000000000000000000080840482169785019790975263ffffffff79010000000000000000000000000000000000000000000000000080850482169786019790975262ffffff7d010000000000000000000000000000000000000000000000000000000000948590041660608601529490970154908116608084015294850490951660a08201529183041660c082015260ff9282048316151560e08201527e0100000000000000000000000000000000000000000000000000000000000090910490911615156101008201529061026a60027f0000000000000000000000000000000000000000000000000000000000000708610653565b826020015164ffffffffff166102809190610653565b604080516101608101825284820180516001601f9190911c8116148252855173ffffffffffffffffffffffffffffffffffffffff9081166020808501919091528088015164ffffffffff908116858701529251633fffffff16606085015260008a8152600482528581208782528083529581205463ffffffff8181166080808901919091529890925295909152640100000000909404841660a080850191909152948701511660c080840191909152938601511660e0808301919091529285015190911661010080830191909152918401511515610120820152920151151561014083015250905092915050565b6000828152600160208181526040808420815161012081018352815473ffffffffffffffffffffffffffffffffffffffff80821683527401000000000000000000000000000000000000000080830464ffffffffff90811697850188905279010000000000000000000000000000000000000000000000000080850463ffffffff818116998801999099527d0100000000000000000000000000000000000000000000000000000000009586900462ffffff166060880152968a015493841660808701529183041660a0850152810490941660c0830152830460ff908116151560e08301527e01000000000000000000000000000000000000000000000000000000000000909304909216151561010083015291928492839283929091601f1c81161461049f5780604001516104a2565b60005b81519650935060006104d560027f0000000000000000000000000000000000000000000000000000000000000708610653565b6104e69064ffffffffff8816610653565b6000988952600460209081526040808b20928b529190529097205495989497509295505063ffffffff8085169464010000000090041692915050565b6000806040838503121561053557600080fd5b50508035926020909101359150565b81511515815261016081016020830151610576602084018273ffffffffffffffffffffffffffffffffffffffff169052565b50604083015161058f604084018264ffffffffff169052565b5060608301516105a7606084018263ffffffff169052565b5060808301516105c3608084018267ffffffffffffffff169052565b5060a08301516105df60a084018267ffffffffffffffff169052565b5060c083015161060760c084018273ffffffffffffffffffffffffffffffffffffffff169052565b5060e083015161062060e084018264ffffffffff169052565b506101008381015163ffffffff169083015261012080840151151590830152610140928301511515929091019190915290565b600082610689577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b50049056fea2646970667358221220adc01ee4985d3283dc32db2a97e9615cf0049fb9a8fa2ff8ecb8577015a0ede764736f6c63430008090033

Block Transaction Gas Used Reward
view all blocks validated

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.