//! On-chain ABI bindings for the contracts integrators most often need when
//! interacting with CoW Protocol.
//!
//! The bindings here are pure type definitions, generated by
//! [`alloy_sol_types::sol!`]. They do not pull in `alloy-contract` or any
//! `Provider` plumbing: callers who want to actually dispatch transactions
//! bring their own `alloy_contract::CallBuilder` (or equivalent) and feed it
//! the call structs exposed below. Tests and off-chain tooling that only need
//! to encode calldata, decode return data or compute selectors can use these
//! types directly.
//!
//! ## What this module exposes
//!
//! - [`GPv2Settlement`]: the `setPreSignature` entry
//!   points used to authorise pre-signed orders, the `settle` entry point
//!   solvers call to clear a batch, the typed `GPv2Order.Data` and
//!   `GPv2Trade.Data` structs, and the on-chain events the contract emits
//!   (`Trade`, `Interaction`, `Settlement`, `OrderInvalidated`,
//!   `PreSignature`). The events let off-chain indexers and MEV tooling
//!   parse settlement transactions without re-deriving the topic hashes.
//! - [`ERC20`]: the subset of the ERC-20 ABI integrators reach for when
//!   approving the vault relayer, checking balances or decimals, and pushing
//!   simple transfers.
//! - [`WETH9`]: `deposit()` and `withdraw(uint256)` on top of the ERC-20
//!   API, for wrapping and unwrapping the chain's native gas token.
//! - [`GPV2_SETTLEMENT`] and [`GPV2_VAULT_RELAYER`]: the two singleton
//!   addresses that share a deployment across every chain CoW Protocol
//!   supports (CREATE2 with the same salt and bytecode).
//!
//! ## Canonical sources
//!
//! - `GPv2Settlement` and `GPv2Order.Data`:
//!   [`cowprotocol/contracts`](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2Settlement.sol)
//!   and
//!   [`GPv2Order.sol`](https://github.com/cowprotocol/contracts/blob/main/src/contracts/libraries/GPv2Order.sol).
//! - Deployment addresses: cow-docs reference (`cow-protocol/reference/contracts/core.mdx`).
//! - `ERC20`: the EIP-20 specification (<https://eips.ethereum.org/EIPS/eip-20>).
//! - `WETH9`: the canonical
//!   [WETH9 source](https://etherscan.io/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2#code).
//!
//! ## A note on `GPv2Order.Data` field types
//!
//! `kind`, `sellTokenBalance` and `buyTokenBalance` are `bytes32` markers in
//! the on-chain Solidity struct, even though the EIP-712 type string hashes
//! them as `string`. The signing-side view lives in
//! `crate::order::eip712::Order` and is routed through
//! [`alloy_sol_types::SolStruct`]. The bindings here mirror the on-chain
//! layout: callers that need the EIP-712 view should use
//! [`crate::OrderData`] instead.

use alloy_primitives::{Address, address};
use alloy_sol_types::sol;

/// Deployment address of the `GPv2Settlement` singleton.
///
/// Identical on every chain CoW Protocol supports thanks to a CREATE2
/// deployment that uses the same salt and bytecode everywhere. Source:
/// `cow-protocol/reference/contracts/core.mdx`.
pub const GPV2_SETTLEMENT: Address = address!("9008D19f58AAbD9eD0D60971565AA8510560ab41");

/// Deployment address of the `GPv2VaultRelayer` singleton, the contract that
/// pulls sell-token allowances on the settlement contract's behalf. This is
/// the spender ERC-20 `approve` calls should target, not the settlement
/// contract itself.
///
/// Identical on every chain CoW Protocol supports. Source:
/// `cow-protocol/reference/contracts/core.mdx`.
pub const GPV2_VAULT_RELAYER: Address = address!("C92E8bdf79f0507f65a392b0ab4667716BFE0110");

sol! {
    /// The 12-field on-chain order struct verified by `GPv2Settlement`.
    ///
    /// Mirrors `GPv2Order.Data` from
    /// [`GPv2Order.sol`](https://github.com/cowprotocol/contracts/blob/main/src/contracts/libraries/GPv2Order.sol):
    ///
    /// ```solidity
    /// struct Data {
    ///     IERC20 sellToken;
    ///     IERC20 buyToken;
    ///     address receiver;
    ///     uint256 sellAmount;
    ///     uint256 buyAmount;
    ///     uint32 validTo;
    ///     bytes32 appData;
    ///     uint256 feeAmount;
    ///     bytes32 kind;
    ///     bool partiallyFillable;
    ///     bytes32 sellTokenBalance;
    ///     bytes32 buyTokenBalance;
    /// }
    /// ```
    ///
    /// `kind`, `sellTokenBalance` and `buyTokenBalance` are `bytes32` here
    /// (matching the storage layout) even though the EIP-712 hash treats them
    /// as `string`. The signing-side typed-data view lives in
    /// `crate::order::eip712::Order` and is fed to
    /// [`alloy_sol_types::SolStruct::eip712_hash_struct`].
    #[derive(Debug)]
    struct GPv2OrderData {
        address sellToken;
        address buyToken;
        address receiver;
        uint256 sellAmount;
        uint256 buyAmount;
        uint32 validTo;
        bytes32 appData;
        uint256 feeAmount;
        bytes32 kind;
        bool partiallyFillable;
        bytes32 sellTokenBalance;
        bytes32 buyTokenBalance;
    }

    /// One element of the `trades` array passed into
    /// [`GPv2Settlement::settle`]. Mirrors `GPv2Trade.Data`:
    ///
    /// ```solidity
    /// struct Data {
    ///     uint256 sellTokenIndex;
    ///     uint256 buyTokenIndex;
    ///     address receiver;
    ///     uint256 sellAmount;
    ///     uint256 buyAmount;
    ///     uint32 validTo;
    ///     bytes32 appData;
    ///     uint256 feeAmount;
    ///     uint256 flags;
    ///     uint256 executedAmount;
    ///     bytes signature;
    /// }
    /// ```
    ///
    /// The `tokens` array at the settlement level is indexed by
    /// `sellTokenIndex` / `buyTokenIndex`; the per-trade
    /// `(sellAmount, buyAmount, feeAmount, executedAmount)` are the
    /// solver-reported amounts at clearing.
    #[derive(Debug)]
    struct GPv2TradeData {
        uint256 sellTokenIndex;
        uint256 buyTokenIndex;
        address receiver;
        uint256 sellAmount;
        uint256 buyAmount;
        uint32 validTo;
        bytes32 appData;
        uint256 feeAmount;
        uint256 flags;
        uint256 executedAmount;
        bytes signature;
    }

    /// One interaction step in [`GPv2Settlement::settle`]'s
    /// `interactions` payload. Mirrors `GPv2Interaction.Data`:
    ///
    /// ```solidity
    /// struct Data {
    ///     address target;
    ///     uint256 value;
    ///     bytes callData;
    /// }
    /// ```
    #[derive(Debug)]
    struct GPv2InteractionData {
        address target;
        uint256 value;
        bytes callData;
    }

    /// Subset of the `GPv2Settlement` ABI integrators most often reach for,
    /// plus the events the contract emits.
    ///
    /// Source:
    /// [`GPv2Settlement.sol`](https://github.com/cowprotocol/contracts/blob/main/src/contracts/GPv2Settlement.sol)
    /// and
    /// [`GPv2Signing.sol`](https://github.com/cowprotocol/contracts/blob/main/src/contracts/mixins/GPv2Signing.sol)
    /// for [`PreSignature`].
    #[derive(Debug)]
    interface GPv2Settlement {
        // --- events ---

        /// Emitted for each executed trade in a settlement batch. Solvers
        /// emit one per settled order.
        event Trade(
            address indexed owner,
            address sellToken,
            address buyToken,
            uint256 sellAmount,
            uint256 buyAmount,
            uint256 feeAmount,
            bytes orderUid
        );

        /// Emitted for each interaction executed during settlement. Only
        /// the first four bytes of the call's selector are recorded, for
        /// gas efficiency; full calldata is recoverable from the settlement
        /// transaction's input.
        event Interaction(address indexed target, uint256 value, bytes4 selector);

        /// Emitted once per settlement transaction, with the solver that
        /// submitted it.
        event Settlement(address indexed solver);

        /// Emitted when an owner invalidates a previously signed order.
        event OrderInvalidated(address indexed owner, bytes orderUid);

        /// Emitted by `GPv2Signing.setPreSignature` whenever a pre-signature
        /// is set or revoked. Carried on the settlement contract address.
        event PreSignature(address indexed owner, bytes orderUid, bool signed);

        // --- functions ---

        /// Settle a batch of orders. Callable only by whitelisted solvers.
        ///
        /// `tokens` is the unique token set referenced by the trades;
        /// `clearingPrices` is the matching price vector (one per token).
        /// `trades` lists the orders being filled; `interactions` is the
        /// three-stage list of pre / intra / post-settlement calls solvers
        /// schedule against external contracts.
        function settle(
            address[] tokens,
            uint256[] clearingPrices,
            GPv2TradeData[] trades,
            GPv2InteractionData[][3] interactions
        ) external;

        /// Toggle the pre-signature flag for `orderUid`.
        ///
        /// When `signed` is true the settlement contract accepts the order as
        /// if it had been signed by the order owner via
        /// [`crate::SigningScheme::PreSign`]; passing false revokes a
        /// previously set pre-signature.
        function setPreSignature(bytes orderUid, bool signed) external;
    }

    /// The subset of the ERC-20 ABI integrators most often need.
    ///
    /// Reference: [EIP-20](https://eips.ethereum.org/EIPS/eip-20).
    #[derive(Debug)]
    interface ERC20 {
        /// Authorise `spender` to move up to `amount` of the caller's balance.
        function approve(address spender, uint256 amount) external returns (bool);

        /// Remaining amount `spender` may still pull from `owner`.
        function allowance(address owner, address spender) external view returns (uint256);

        /// Token balance held by `owner`.
        function balanceOf(address owner) external view returns (uint256);

        /// Move `amount` from the caller to `to`.
        function transfer(address to, uint256 amount) external returns (bool);

        /// Token's display decimals.
        function decimals() external view returns (uint8);
    }

    /// The two non-ERC-20 entry points on canonical
    /// [WETH9](https://etherscan.io/address/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2#code).
    /// The ERC-20 functions (`approve`, `balanceOf`, etc.) are exposed
    /// through [`ERC20`].
    #[derive(Debug)]
    interface WETH9 {
        /// Wrap `msg.value` of the chain's native gas token into WETH credited
        /// to the caller.
        function deposit() external payable;

        /// Burn `wad` WETH from the caller and pay out the equivalent in the
        /// chain's native gas token.
        function withdraw(uint256 wad) external;
    }

    /// On-chain signature scheme variants accepted by
    /// [`CoWSwapOnchainOrders`]. Mirrors the `OnchainSigningScheme`
    /// Solidity enum at
    /// [`CoWSwapOnchainOrders.sol`](https://github.com/cowprotocol/ethflowcontract/blob/main/src/mixins/CoWSwapOnchainOrders.sol).
    #[derive(Debug, Eq, PartialEq)]
    enum OnchainSigningScheme {
        /// EIP-1271 contract signature (the placer is a contract that
        /// implements `isValidSignature`).
        Eip1271,
        /// Pre-signature recorded on the settlement contract via
        /// `GPv2Signing::setPreSignature`.
        PreSign,
    }

    /// Signature payload accompanying an on-chain order placement.
    /// Mirrors `CoWSwapOnchainOrders.OnchainSignature`.
    #[derive(Debug, Eq, PartialEq)]
    struct OnchainSignature {
        OnchainSigningScheme scheme;
        bytes data;
    }

    /// Events emitted by `CoWSwapOnchainOrders`, the mixin every ETH-flow
    /// or contract-placed-order entry point inherits. Off-chain indexers
    /// watch [`OrderPlacement`] to learn that a new on-chain order has
    /// been registered and [`OrderInvalidation`] to mark it cancelled.
    ///
    /// Source:
    /// [`CoWSwapOnchainOrders.sol`](https://github.com/cowprotocol/ethflowcontract/blob/main/src/mixins/CoWSwapOnchainOrders.sol).
    #[derive(Debug)]
    interface CoWSwapOnchainOrders {
        /// Emitted when a contract registers a new on-chain order. The
        /// embedded `GPv2OrderData` is the exact 12-field payload the
        /// settlement contract will verify; `signature` carries the
        /// scheme (`PreSign` or `EIP-1271`) and any contract-specific
        /// payload; `data` is opaque metadata the contract chose to
        /// publish (e.g. ETH-flow's refund pointer).
        event OrderPlacement(
            address indexed sender,
            GPv2OrderData order,
            OnchainSignature signature,
            bytes data
        );

        /// Emitted when a previously placed on-chain order is
        /// invalidated, either by the placer or by an authorised
        /// invalidator. The body is the 56-byte order UID.
        ///
        /// Note the naming overlap with [`GPv2Settlement::OrderInvalidated`]:
        /// the two events live on different contracts (the settlement
        /// singleton vs the ETH-flow / on-chain-orders mixin) and have
        /// different topic hashes. Indexers should match on
        /// `log.topics[0]` against the canonical signature hash of the
        /// event they actually care about.
        event OrderInvalidation(bytes orderUid);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::{Bytes, keccak256};
    use alloy_sol_types::{SolCall, SolEvent};

    /// `GPv2Settlement::setPreSignature(bytes,bool)` must encode to the same
    /// 4-byte selector as `keccak256("setPreSignature(bytes,bool)")[..4]`.
    /// This is what relayers and solvers match against when decoding
    /// settlement-bound transactions.
    #[test]
    fn set_pre_signature_selector_matches_keccak() {
        let expected = &keccak256("setPreSignature(bytes,bool)")[..4];
        assert_eq!(&GPv2Settlement::setPreSignatureCall::SELECTOR, expected);
    }

    /// Encoding `setPreSignature((uid, true))` must round-trip back to the
    /// same `(uid, true)` and start with the canonical selector. This locks
    /// the call's wire format against accidental field reordering.
    #[test]
    fn set_pre_signature_call_round_trips() {
        let uid = Bytes::from(vec![0xab; 56]);
        let call = GPv2Settlement::setPreSignatureCall {
            orderUid: uid.clone(),
            signed: true,
        };
        let encoded = call.abi_encode();
        assert_eq!(
            &encoded[..4],
            &GPv2Settlement::setPreSignatureCall::SELECTOR
        );
        let decoded = GPv2Settlement::setPreSignatureCall::abi_decode(&encoded).unwrap();
        assert_eq!(decoded.orderUid, uid);
        assert!(decoded.signed);
    }

    /// `ERC20::approve(address,uint256)` must encode to the well-known
    /// `0x095ea7b3` selector that every block explorer recognises.
    #[test]
    fn erc20_approve_selector_matches_keccak() {
        let expected = &keccak256("approve(address,uint256)")[..4];
        assert_eq!(&ERC20::approveCall::SELECTOR, expected);
        assert_eq!(ERC20::approveCall::SELECTOR, [0x09, 0x5e, 0xa7, 0xb3]);
    }

    /// `WETH9::deposit()` must encode to the well-known `0xd0e30db0`
    /// selector used by every WETH wrapper in the wild.
    #[test]
    fn weth9_deposit_selector_matches_keccak() {
        let expected = &keccak256("deposit()")[..4];
        assert_eq!(&WETH9::depositCall::SELECTOR, expected);
        assert_eq!(WETH9::depositCall::SELECTOR, [0xd0, 0xe3, 0x0d, 0xb0]);
    }

    /// `settle(address[],uint256[],GPv2TradeData[],GPv2InteractionData[][3])`
    /// must compute to the canonical 4-byte selector `0x13d79a0b` that
    /// solvers and block explorers match against. Locks the typed Rust
    /// signature against the on-chain ABI.
    #[test]
    fn settle_selector_matches_keccak() {
        let expected = &keccak256(
            "settle(address[],uint256[],(uint256,uint256,address,uint256,uint256,uint32,bytes32,uint256,uint256,uint256,bytes)[],(address,uint256,bytes)[][3])",
        )[..4];
        assert_eq!(&GPv2Settlement::settleCall::SELECTOR, expected);
        assert_eq!(
            GPv2Settlement::settleCall::SELECTOR,
            [0x13, 0xd7, 0x9a, 0x0b]
        );
    }

    /// The five `GPv2Settlement` event topic hashes must match the
    /// canonical `keccak256(signature)` values, so off-chain indexers
    /// matching `log.topics[0]` against this Rust constant pick up every
    /// settlement event without drift.
    #[test]
    fn settlement_event_topic_hashes_match_keccak() {
        // Trade(address,address,address,uint256,uint256,uint256,bytes)
        assert_eq!(
            GPv2Settlement::Trade::SIGNATURE_HASH,
            keccak256("Trade(address,address,address,uint256,uint256,uint256,bytes)")
        );
        // Interaction(address,uint256,bytes4)
        assert_eq!(
            GPv2Settlement::Interaction::SIGNATURE_HASH,
            keccak256("Interaction(address,uint256,bytes4)")
        );
        // Settlement(address)
        assert_eq!(
            GPv2Settlement::Settlement::SIGNATURE_HASH,
            keccak256("Settlement(address)")
        );
        // OrderInvalidated(address,bytes)
        assert_eq!(
            GPv2Settlement::OrderInvalidated::SIGNATURE_HASH,
            keccak256("OrderInvalidated(address,bytes)")
        );
        // PreSignature(address,bytes,bool)
        assert_eq!(
            GPv2Settlement::PreSignature::SIGNATURE_HASH,
            keccak256("PreSignature(address,bytes,bool)")
        );
    }

    /// `GPv2Settlement::Trade` log round-trips: encode the data segment,
    /// decode it back, and verify every non-indexed field. The indexed
    /// `owner` rides in `topics[1]` so it is verified separately by
    /// callers that decode logs end-to-end.
    #[test]
    fn trade_event_data_round_trips() {
        let event = GPv2Settlement::Trade {
            owner: address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"),
            sellToken: address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
            buyToken: address!("6B175474E89094C44Da98b954EedeAC495271d0F"),
            sellAmount: alloy_primitives::U256::from(1_000_000u64),
            buyAmount: alloy_primitives::U256::from(999_000_000_000_000_000u128),
            feeAmount: alloy_primitives::U256::from(123u64),
            orderUid: Bytes::from(vec![0xab; 56]),
        };
        let data = event.encode_data();
        let decoded = GPv2Settlement::Trade::abi_decode_data(&data).unwrap();
        // abi_decode_data returns a tuple of the non-indexed fields, in
        // declaration order: (sellToken, buyToken, sellAmount, buyAmount,
        // feeAmount, orderUid).
        assert_eq!(decoded.0, event.sellToken);
        assert_eq!(decoded.1, event.buyToken);
        assert_eq!(decoded.2, event.sellAmount);
        assert_eq!(decoded.3, event.buyAmount);
        assert_eq!(decoded.4, event.feeAmount);
        assert_eq!(decoded.5, event.orderUid);
    }

    /// Lock the `CoWSwapOnchainOrders` event topic hashes against the
    /// canonical Solidity signatures so off-chain indexers matching
    /// `log.topics[0]` against these Rust constants pick up every emitted
    /// event from the ETH-flow and on-chain-orders mixin.
    #[test]
    fn cowswap_onchain_orders_event_topic_hashes_match_keccak() {
        assert_eq!(
            CoWSwapOnchainOrders::OrderPlacement::SIGNATURE_HASH,
            keccak256(
                "OrderPlacement(address,(address,address,address,uint256,uint256,\
                 uint32,bytes32,uint256,bytes32,bool,bytes32,bytes32),(uint8,bytes),bytes)"
            )
        );
        assert_eq!(
            CoWSwapOnchainOrders::OrderInvalidation::SIGNATURE_HASH,
            keccak256("OrderInvalidation(bytes)")
        );
    }

    /// Pin the GPv2 deployment hex literals so a copy-paste regression
    /// on the constants (e.g. relayer overwriting settlement) breaks the
    /// build instead of silently shipping orders to the wrong contract.
    #[test]
    fn deployment_addresses_match_canonical_hex_literals() {
        assert_eq!(
            GPV2_SETTLEMENT,
            address!("9008D19f58AAbD9eD0D60971565AA8510560ab41")
        );
        assert_eq!(
            GPV2_VAULT_RELAYER,
            address!("C92E8bdf79f0507f65a392b0ab4667716BFE0110")
        );
        assert_ne!(GPV2_SETTLEMENT, GPV2_VAULT_RELAYER);
    }
}
