tycho-ethereum 0.303.2

Ethereum specific implementation of core tycho traits
Documentation
/// EVM runtime bytecodes for the Analyzer and Forwarder contracts used by the
/// eth_call-based token analyzer.
///
/// Both contracts are injected at runtime via `eth_call` state overrides — no on-chain
/// deployment is required. The Analyzer is injected at the token holder's address so that
/// `msg.sender` for the inbound `transfer()` call is the actual holder (who has the balance).
/// The Forwarder is injected at the settlement address so that outbound transfers can be
/// proxied from there.
///
/// # Recompiling
///
/// Source files live in `tycho-ethereum/contracts/`. Both were compiled with:
/// `solc 0.8.34 --bin-runtime --via-ir --optimize --optimize-runs 200 --no-cbor-metadata
/// --evm-version berlin`
///
/// Note: compiled with berlin EVM version because we run this on old blocks.
///
/// ```sh
/// solc --bin-runtime --via-ir --optimize --optimize-runs 200 --no-cbor-metadata --evm-version berlin Analyzer.sol
/// solc --bin-runtime --via-ir --optimize --optimize-runs 200 --no-cbor-metadata --evm-version berlin Forwarder.sol
/// ```
///
/// Update the `hex!` constants below with the new output.
use alloy::sol;

/// Runtime bytecode for `contracts/Analyzer.sol`.
pub const ANALYZER_BYTECODE: &[u8] = &alloy::primitives::hex!(
    "6080604052600436101561001257600080fd5b6000803560e01c63521c65391461002857600080fd5b346100bf5760803660031901126100bf576001600160a01b039060043582811681036100c2576044359183831683036100bf5760643593841684036100bf5761014061007885856024358661013e565b976040979197969296959395519915158a52151560208a0152151560408901526060880152608087015260a086015260c085015260e0840152610100830152610120820152f35b80fd5b5080fd5b90601f8019910116810190811067ffffffffffffffff8211176100e857604052565b634e487b7160e01b600052604160045260246000fd5b90816020910312610116575180151581036101165790565b600080fd5b9190820391821161012857565b634e487b7160e01b600052601160045260246000fd5b92939160018060a01b03948585169460408051946370a0823160e01b9384875289600496169889878901526020602491818a8481845afa998a156105cd5760009a61059e575b50898287519e8f968b88521695868c8201528581855afa9081156104dc578d9e60009261056c575b5081985a9e895198868a019163a9059cbb60e01b8352888b015260448a015260448952608089019867ffffffffffffffff998181108b821117610557578b525160009283929083905af1963d1561054f573d90811161053b5788519061021b601f8201601f19168701836100c6565b81523d60008683013e5b87610508575b50610238879e5a9061011b565b96156104e7575050855198888a528d818b0152828a8581855afa998a156104dc5760009a6104ad575b50898b811061049157506102bf9c9d61027a8c8c61011b565b955a96895190631339c64960e01b825285858301528288830152604482015285816064816000875af160009181610472575b5061046a575060009e8f975b5a9061011b565b96156103ee5788518b8152828482015285818881885afa9081156103b6576000916103c1575b509a8951908152818482015285818881885afa9081156103b65790869594939291600091610380575b5091600091606494939b5198899687956361be88e160e11b8752860152840152811960448401525af1918291600093610351575b505061034e5750600097565b97565b610371929350803d10610379575b61036981836100c6565b8101906100fe565b903880610342565b503d61035f565b86819395949792503d83116103af575b61039a81836100c6565b8101031261011657518594919290600061030e565b503d610390565b8a513d6000823e3d90fd5b90508581813d83116103e7575b6103d881836100c6565b810103126101165751386102e5565b503d6103ce565b509683969e50809a9b9c989992919493959d5051968794859384528301525afa9485156104605750600094610430575b50600197600097889796959493889350565b9080929450813d8311610459575b61044881836100c6565b81010312610116575191388061041e565b503d61043e565b513d6000823e3d90fd5b9e8f976102b8565b61048a919250873d89116103795761036981836100c6565b90386102ac565b60009e508e9d508d9c909a508c99508997508795509350505050565b90998382813d83116104d5575b6104c481836100c6565b810103126100bf5750519838610261565b503d6104ba565b87513d6000823e3d90fd5b60009e508e9d508d9c909b508c9a508a995090975088965086945092505050565b809197505190848215928315610523575b505050953861022b565b61053393508201810191016100fe565b388481610519565b8560418d634e487b7160e01b600052526000fd5b506060610225565b508760418f634e487b7160e01b600052526000fd5b9d509050828d813d8111610597575b61058581836100c6565b81010312610116578d9c5190386101ac565b503d61057b565b9099508181813d83116105c6575b6105b681836100c6565b8101031261011657519838610184565b503d6105ac565b86513d6000823e3d90fd"
);

/// Runtime bytecode for `contracts/Forwarder.sol`.
pub const FORWARDER_BYTECODE: &[u8] = &alloy::primitives::hex!(
    "60806040818152600436101561001457600080fd5b600091823560e01c9081631339c649146100e1575063c37d11c21461003857600080fd5b346100dd5761007e90602061004c366101d2565b845163095ea7b360e01b81526001600160a01b0390921660048301526024820152938491908290879082906044820190565b03926001600160a01b03165af19182156100d357602093926100a4575b50519015158152f35b6100c5919250833d85116100cc575b6100bd818361020c565b810190610244565b903861009b565b503d6100b3565b81513d85823e3d90fd5b5080fd5b919050346101ce5782806100f4366101d2565b63a9059cbb60e01b60208881019182526001600160a01b039390931660248901526044808901929092529087529096601f199690929190839061013860648261020c565b51925af1923d156101c5573d67ffffffffffffffff81116101b15761016686855193601f840116018361020c565b81528091853d92013e5b8261017e5750519015158152f35b809192505190838215928315610199575b505050903861009b565b6101a99350820181019101610244565b38838161018f565b634e487b7160e01b83526041600452602483fd5b50506060610170565b8280fd5b6060906003190112610207576001600160a01b0390600435828116810361020757916024359081168103610207579060443590565b600080fd5b90601f8019910116810190811067ffffffffffffffff82111761022e57604052565b634e487b7160e01b600052604160045260246000fd5b9081602091031261020757518015158103610207579056"
);

// ABI for the Analyzer's analyze() function. Used to encode calldata and decode the return value.
sol! {
    function analyze(
        address token,
        uint256 amount,
        address settlement,
        address recipient
    ) external returns (
        bool transferInOk,
        bool transferOutOk,
        bool approvalOk,
        uint256 balanceBeforeIn,
        uint256 balanceAfterIn,
        uint256 balanceAfterOut,
        uint256 recipientBefore,
        uint256 recipientAfter,
        uint256 gasIn,
        uint256 gasOut
    );
}

#[cfg(test)]
mod tests {
    use alloy::sol_types::SolCall;

    use super::*;

    #[test]
    fn bytecodes_are_valid_evm() {
        // EVM contracts compiled via --via-ir start with PUSH1 0x80 (0x60 0x80).
        assert!(ANALYZER_BYTECODE.len() > 10, "analyzer bytecode is suspiciously short");
        assert_eq!(
            &ANALYZER_BYTECODE[..2],
            &[0x60, 0x80],
            "analyzer bytecode has wrong EVM header"
        );

        assert!(FORWARDER_BYTECODE.len() > 10, "forwarder bytecode is suspiciously short");
        assert_eq!(
            &FORWARDER_BYTECODE[..2],
            &[0x60, 0x80],
            "forwarder bytecode has wrong EVM header"
        );
    }

    #[test]
    fn analyze_abi_encodes_to_correct_selector() {
        // Function selector for analyze(address,uint256,address,address) = keccak256[..4]
        // Expected: 0x521c6539
        let calldata = analyzeCall {
            token: alloy::primitives::Address::ZERO,
            amount: alloy::primitives::U256::ZERO,
            settlement: alloy::primitives::Address::ZERO,
            recipient: alloy::primitives::Address::ZERO,
        }
        .abi_encode();

        assert_eq!(&calldata[..4], &[0x52, 0x1c, 0x65, 0x39]);
    }
}