Robinhood MainnetChain ID: 4663
Contract detail

X402ProxyFacilitatorV3

X402ProxyFacilitatorV3 is a verified Robinhood Chain smart contract at 0xa1e5cb6f…fafe2c41 with 0 indexed transactions.

Address type
Smart contract
Transactions
0
History coverage
No indexed activity found
Emitted events
1
Verification
Verified
Compiler
v0.8.27+commit.40a35a09
Verification match
partial
Creation transaction
0x201e32fb…c2021c35
Created at block
#850358
Language
solidity
EVM version
cancun
License
none
Compiler optimization
Enabled · 200 runs
Verified contract

Contract source code

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {UD60x18, ud, ZERO} from "prb-math/UD60x18.sol";
import {exp} from "prb-math/ud60x18/Math.sol";
import {mulDiv} from "prb-math/Common.sol";

import "./libraries/TickMath.sol";

interface IFiatTokenV2 {
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes memory signature
    ) external;
}

// Aerodrome Pool interface for TWAP
interface IAerodromePool {
    function observe(uint32[] calldata secondsAgos)
        external
        view
        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            bool unlocked
        );
    function token0() external view returns (address);
    function token1() external view returns (address);
}

interface IAcrossSpokePool {
    function depositV3(
        address depositor,
        address recipient,
        address inputToken,
        address outputToken,
        uint256 inputAmount,
        uint256 outputAmount,
        uint256 destinationChainId,
        address exclusiveRelayer,
        uint32 quoteTimestamp,
        uint32 fillDeadline,
        uint32 exclusivityDeadline,
        bytes calldata message
    ) external payable;
}

contract X402ProxyFacilitatorV3 is
    Initializable,
    OwnableUpgradeable,
    PausableUpgradeable,
    UUPSUpgradeable,
    EIP712Upgradeable,
    ReentrancyGuardUpgradeable
{
    using SafeERC20 for IERC20;
    uint256 public constant CASHBACK_SUPPLY = 700_000_000 * 1e18; // 700M tokens with 18 decimals

    // Fee parameters
    uint256 public constant BASIS_POINTS_DIVISOR = 10000;
    uint256 public constant MAX_FEE_BPS = 1000; // 10% max fee
    uint256 public treasuryFeeBps;

    // Nested mapping to track recipient balances per token
    // recipientBalances[tokenAddress][walletAddress] = balance
    mapping(address => mapping(address => uint256)) public recipientBalances;

    // Track platform fees collected per token per platform address
    // platformFees[tokenAddress][platformAddress] = balance
    mapping(address => mapping(address => uint256)) public platformFees;

    address public treasury;
    address public usdcContract;
    address public mrdnToken;
    address public aerodromePool; // MRDN/USDC pool
    uint32 public twapWindow; // TWAP window in seconds

    // Cashback parameters
    uint256 public constant CASHBACK_SCALE_FACTOR = 1e18; // For precision
    uint256 public constant INITIAL_CASHBACK_RATE = 2e16; // 0.02 = 2%
    uint256 public decayConstant; // Decay constant in UD60x18 format (e.g., 6.9e9 for 6.9e-9)
    uint256 public totalMrdnCashbackDistributed;

    // Cross chain
    uint256 constant BASE_CHAIN_ID = 8453;
    mapping(uint256 => address) public remoteUsdc;

    // Backend signer for Across message authenticity (service-signed)
    address public backendSigner;

    // Track used nonces for Across messages to prevent replay
    mapping(bytes32 => bool) public usedAcrossNonces;

    struct AcrossDepositParams {
        uint256 destinationChainId;
        uint256 outputAmount;
        uint32 quoteTimestamp;
        uint32 fillDeadline;
    }

    address public acrossSpokePool;

    // Track treasury fees collected per token (e.g., USDC)
    mapping(address => uint256) public treasuryFees;

    // Across analytics: append delimiter + unique identifier to depositV3 calldata
    // Delimiter specified by Across: 0x1dc0de
    bytes3 private constant ACROSS_CALLDATA_DELIMITER = 0x1dc0de;
    bytes2 private constant ACROSS_UNIQUE_ID = 0x00a3; // Provided by Across for this integrator

    // Across message authenticity is always enforced via depositor EIP-712 signature

    // EIP-712 typehash for service-signed Across messages
    // keccak256("AcrossMessage(address originalSender,address creditedRecipient,address platform,uint256 expectedAmount,uint256 platformFeeBps,bytes32 nonce,uint256 sourceChainId,uint256 destinationChainId,address token,address recipientContract)")
    // = 0x74800e6f1923b7ffd17d20ac98efb76f3f17d2e91699aa88f85d0da2160d8399
    bytes32 private constant ACROSS_MESSAGE_TYPEHASH =
        0x74800e6f1923b7ffd17d20ac98efb76f3f17d2e91699aa88f85d0da2160d8399;

    // Events
    event TransferWithAuthorizationForwarded(
        address indexed from,
        address indexed to,
        uint256 value,
        bytes32 nonce,
        address indexed platform,
        uint256 platformFeeBps,
        uint256 sourceChainId,
        uint256 destinationChainId
    );
    event PlatformFeeWithdrawn(address indexed platform, address indexed token, uint256 amount);
    event TreasuryFeesWithdrawn(address indexed token, uint256 amount, address indexed to);
    event InstantSettlement(
        address indexed from,
        address indexed recipient,
        address indexed platform,
        uint256 grossAmount,
        uint256 platformFee,
        uint256 treasuryFee,
        uint256 netAmount,
        uint256 sourceChainId,
        uint256 destinationChainId
    );

    event FeeUpdated(uint256 newTreasuryFeeBps);
    event TWAPWindowUpdated(uint32 newWindow);
    event RemoteUsdcUpdated(uint256 indexed chainId, address remoteUsdc);
    event CashbackPaid(
        address indexed recipient,
        uint256 usdcAmount,
        uint256 mrdnAmount,
        uint256 indexed sourceChainId,
        uint256 indexed destinationChainId
    );
    event BackendSignerUpdated(address signer);

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    modifier onlyAcrossSpokePool() {
        require(msg.sender == acrossSpokePool, "not across");
        _;
    }

    /// @custom:oz-upgrades-unsafe-allow missing-initializer-call
    /// @custom:oz-upgrades-validate-as-initializer
    function initializeV3() public reinitializer(3) {
        __EIP712_init("X402ProxyFacilitatorV3", "1");
    }

    /* Restricted functions */

    function emergencyWithdraw(address token, uint256 amount) external onlyOwner {
        IERC20(token).safeTransfer(owner(), amount);
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

    function setTreasuryFeeBps(uint256 newTreasuryFeeBps) external onlyOwner {
        require(newTreasuryFeeBps <= MAX_FEE_BPS, "Fee too high");
        treasuryFeeBps = newTreasuryFeeBps;
        emit FeeUpdated(newTreasuryFeeBps);
    }

    function setTWAPWindow(uint32 newWindow) external onlyOwner {
        require(newWindow >= 60, "Window too short"); // At least 1 minute
        require(newWindow <= 3600, "Window too long"); // Max 1 hour
        twapWindow = newWindow;
        emit TWAPWindowUpdated(newWindow);
    }

    function pause() external onlyOwner {
        _pause();
    }

    function unpause() external onlyOwner {
        _unpause();
    }

    function setRemoteUsdc(uint256 chainId, address remoteUsdc_) external onlyOwner {
        require(chainId != block.chainid, "invalid chain");
        require(remoteUsdc_ != address(0), "invalid usdc");

        remoteUsdc[chainId] = remoteUsdc_;

        emit RemoteUsdcUpdated(chainId, remoteUsdc_);
    }

    function setAcrossSpokePool(address _spoke) external onlyOwner {
        require(_spoke != address(0), "invalid spoke");
        acrossSpokePool = _spoke;
    }

    function setBackendSigner(address signer) external onlyOwner {
        require(signer != address(0), "invalid signer");
        backendSigner = signer;
        emit BackendSignerUpdated(signer);
    }

    // Unique identifier is hardcoded via ACROSS_UNIQUE_ID

    /* Public functions */

    function transferWithAuthorization(
        address from,
        address,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes memory signature,
        address recipient,
        address platform,
        uint256 platformFeeBps
    ) external whenNotPaused nonReentrant {
        require(recipient != address(0), "invalid recipient");
        IFiatTokenV2(usdcContract)
            .transferWithAuthorization(from, address(this), value, validAfter, validBefore, nonce, signature);

        _handleFeesAndCashback(from, recipient, platform, value, platformFeeBps, block.chainid, block.chainid);

        emit TransferWithAuthorizationForwarded(
            from, address(this), value, nonce, platform, platformFeeBps, block.chainid, block.chainid
        );
    }

    /// @notice Cross-chain transfer with depositor EIP-712 signature included in Across payload
    function transferWithAuthorization(
        address from,
        address,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        bytes memory signature,
        address recipient,
        address platform,
        uint256 platformFeeBps,
        AcrossDepositParams calldata depositParams,
        bytes calldata backendMessageSig
    ) external whenNotPaused nonReentrant {
        require(recipient != address(0), "invalid recipient");
        uint256 destChain = depositParams.destinationChainId;
        require(destChain != 0, "invalid dest");

        IFiatTokenV2(usdcContract)
            .transferWithAuthorization(from, address(this), value, validAfter, validBefore, nonce, signature);

        if (destChain == block.chainid) {
            // Same-chain: no Across bridging. Process locally.
            _handleFeesAndCashback(from, recipient, platform, value, platformFeeBps, block.chainid, block.chainid);

            emit TransferWithAuthorizationForwarded(
                from, address(this), value, nonce, platform, platformFeeBps, block.chainid, block.chainid
            );
        } else {
            // Cross-chain: requires backend signature and Across deposit
            _bridgeViaAcross(from, recipient, platform, value, platformFeeBps, nonce, depositParams, backendMessageSig);

            emit TransferWithAuthorizationForwarded(
                from, address(this), value, nonce, platform, platformFeeBps, block.chainid, destChain
            );
        }
    }

    function handleV3AcrossMessage(
        address tokenSent,
        uint256 amount,
        address relayer, // relayer - set to address(0) to allow all relayers
        bytes calldata message
    )
        external
        onlyAcrossSpokePool
        whenNotPaused
        nonReentrant
    {
        require(tokenSent == usdcContract, "unexpected token");

        (
            address originalSender,
            address creditedRecipient,
            address platform,
            uint256 expectedAmount,
            uint256 platformFeeBps,
            bytes32 nonce_,
            uint256 sourceChainId,
            bytes memory backendSig
        ) = abi.decode(message, (address, address, address, uint256, uint256, bytes32, uint256, bytes));

        require(amount == expectedAmount, "amount mismatch");
        require(remoteUsdc[sourceChainId] != address(0), "unconf src");

        require(
            _verifyAcrossMessageSignature(
                originalSender,
                creditedRecipient,
                platform,
                expectedAmount,
                platformFeeBps,
                nonce_,
                sourceChainId,
                block.chainid,
                usdcContract,
                address(this),
                backendSig
            ),
            "bad backend sig"
        );

        // Now that the message is authenticated, mark nonce as used
        require(!usedAcrossNonces[nonce_], "nonce used");
        usedAcrossNonces[nonce_] = true;

        _handleFeesAndCashback(
            originalSender, creditedRecipient, platform, amount, platformFeeBps, sourceChainId, block.chainid
        );

        emit TransferWithAuthorizationForwarded(
            originalSender, address(this), amount, nonce_, platform, platformFeeBps, sourceChainId, block.chainid
        );
    }

    function _bridgeViaAcross(
        address from,
        address recipient,
        address platform,
        uint256 amount,
        uint256 platformFeeBps,
        bytes32 nonce,
        AcrossDepositParams memory depositParams,
        bytes memory backendMessageSig
    ) internal {
        require(backendMessageSig.length != 0, "signature required");
        uint256 destChain = depositParams.destinationChainId;
        address remoteUsdcAddress = remoteUsdc[destChain];
        require(remoteUsdcAddress != address(0), "bridge config missing");

        address spokePool = acrossSpokePool;
        require(spokePool != address(0), "across not configured");

        uint32 quoteTimestamp = depositParams.quoteTimestamp;
        uint32 fillDeadline = depositParams.fillDeadline;
        require(uint256(fillDeadline) > block.timestamp, "fill deadline passed");
        require(uint256(quoteTimestamp) <= block.timestamp, "future quote");
        require(uint256(fillDeadline) >= uint256(quoteTimestamp), "bad window");

        // Use SafeERC20 to handle tokens with non-standard approve behavior
        IERC20(usdcContract).forceApprove(spokePool, amount);

        // Build depositV3 call data, append Across delimiter (0x1dc0de) and our unique identifier to calldata only
        // Do NOT include the delimiter/ID inside any depositV3 parameter (including message).
        bytes memory messageBytes = abi.encode(
            from,
            recipient,
            platform,
            depositParams.outputAmount,
            platformFeeBps,
            nonce,
            block.chainid,
            backendMessageSig
        );

        bytes memory encoded = abi.encodeWithSelector(
            IAcrossSpokePool.depositV3.selector,
            from,
            address(this),
            usdcContract,
            remoteUsdcAddress,
            amount,
            depositParams.outputAmount,
            destChain,
            address(0),
            quoteTimestamp,
            fillDeadline,
            0,
            messageBytes
        );

        // Append delimiter + unique ID to calldata and perform low-level call
        bytes memory fullCalldata = abi.encodePacked(encoded, ACROSS_CALLDATA_DELIMITER, ACROSS_UNIQUE_ID);
        (bool ok,) = address(spokePool).call(fullCalldata);
        require(ok, "across deposit failed");
    }

    function _handleFeesAndCashback(
        address from,
        address recipient,
        address platform,
        uint256 value,
        uint256 platformFeeBps,
        uint256 sourceChainId,
        uint256 destinationChainId
    ) internal {
        _settleAndDistribute(from, recipient, platform, value, platformFeeBps, sourceChainId, destinationChainId);
        _routeCashback(from, recipient, value, sourceChainId, destinationChainId);
    }

    function _settleAndDistribute(
        address from,
        address recipient,
        address platform,
        uint256 value,
        uint256 platformFeeBps,
        uint256 sourceChainId,
        uint256 destinationChainId
    ) internal {
        uint256 contractBalance = IERC20(usdcContract).balanceOf(address(this));
        require(contractBalance >= value, "Insufficient contract balance");

        uint256 platformFee = 0;
        uint256 recipientPortion = value;

        if (platform != address(0) && platformFeeBps > 0) {
            require(platformFeeBps <= MAX_FEE_BPS, "Platform fee too high");
            platformFee = (value * platformFeeBps) / BASIS_POINTS_DIVISOR;
            recipientPortion = value - platformFee;
            platformFees[usdcContract][platform] += platformFee;
        }

        // Treasury fee applies after platform fee (maintains V2 economics)
        uint256 treasuryFee = (recipientPortion * treasuryFeeBps) / BASIS_POINTS_DIVISOR;
        uint256 netAmount = recipientPortion - treasuryFee;
        treasuryFees[usdcContract] += treasuryFee;

        IERC20(usdcContract).safeTransfer(recipient, netAmount);

        emit InstantSettlement(
            from, recipient, platform, value, platformFee, treasuryFee, netAmount, sourceChainId, destinationChainId
        );
    }

    function _routeCashback(
        address from,
        address recipient,
        uint256 value,
        uint256 sourceChainId,
        uint256 destinationChainId
    ) internal {
        // Cashback is only enabled on Base (destination must be Base)
        if (_isBaseChain()) {
            uint256 cashbackAmount = _calculateAndSendCashback(recipient, value);
            if (cashbackAmount > 0) {
                emit CashbackPaid(recipient, value, cashbackAmount, sourceChainId, destinationChainId);
            }
        }
    }

    /* Across message helpers */

    /// @notice Generate the EIP-712 digest for an Across message (for testing/debugging)
    /// @dev This returns the digest that would be signed by the backend
    function generateBackendSigDigest(
        address originalSender,
        address creditedRecipient,
        address platform,
        uint256 expectedAmount,
        uint256 platformFeeBps,
        bytes32 nonce,
        uint256 sourceChainId,
        uint256 destinationChainId,
        address token,
        address recipientContract
    ) external view returns (bytes32) {
        bytes32 structHash = keccak256(
            abi.encode(
                ACROSS_MESSAGE_TYPEHASH,
                originalSender,
                creditedRecipient,
                platform,
                expectedAmount,
                platformFeeBps,
                nonce,
                sourceChainId,
                destinationChainId,
                token,
                recipientContract
            )
        );
        return _hashTypedDataV4(structHash);
    }

    /// @notice Public function to verify an Across message signature (for testing/debugging)
    /// @dev This is a public wrapper around the internal _verifyAcrossMessageSignature function
    function verifyAcrossMessageSignature(
        address originalSender,
        address creditedRecipient,
        address platform,
        uint256 expectedAmount,
        uint256 platformFeeBps,
        bytes32 nonce,
        uint256 sourceChainId,
        uint256 destinationChainId,
        address token,
        address recipientContract,
        bytes memory backendSig
    ) external view returns (bool) {
        return _verifyAcrossMessageSignature(
            originalSender,
            creditedRecipient,
            platform,
            expectedAmount,
            platformFeeBps,
            nonce,
            sourceChainId,
            destinationChainId,
            token,
            recipientContract,
            backendSig
        );
    }

    function _verifyAcrossMessageSignature(
        address originalSender,
        address creditedRecipient,
        address platform,
        uint256 expectedAmount,
        uint256 platformFeeBps,
        bytes32 nonce,
        uint256 sourceChainId,
        uint256 destinationChainId,
        address token,
        address recipientContract,
        bytes memory backendSig
    ) internal view returns (bool) {
        require(backendSigner != address(0), "signer not set");
        bytes32 structHash = keccak256(
            abi.encode(
                ACROSS_MESSAGE_TYPEHASH,
                originalSender,
                creditedRecipient,
                platform,
                expectedAmount,
                platformFeeBps,
                nonce,
                sourceChainId,
                destinationChainId,
                token,
                recipientContract
            )
        );
        bytes32 digest = _hashTypedDataV4(structHash);
        address recovered = ECDSA.recover(digest, backendSig);
        return recovered == backendSigner;
    }

    /* Withdrawal functions */

    function withdrawPlatformFees(address token) external nonReentrant {
        uint256 platformBalance = platformFees[token][msg.sender];
        require(platformBalance > 0, "No platform fees to withdraw");

        // Check that the contract has sufficient token balance
        uint256 contractBalance = IERC20(token).balanceOf(address(this));
        require(contractBalance >= platformBalance, "Insufficient contract balance");

        // Reset platform balance for this token
        platformFees[token][msg.sender] = 0;

        IERC20(token).safeTransfer(msg.sender, platformBalance);

        emit PlatformFeeWithdrawn(msg.sender, token, platformBalance);
    }

    function withdrawTreasuryFees(address token) external {
        uint256 amount = treasuryFees[token];
        require(amount > 0, "No treasury fees");

        treasuryFees[token] = 0;
        IERC20(token).safeTransfer(treasury, amount);

        emit TreasuryFeesWithdrawn(token, amount, treasury);
    }

    function _isBaseChain() internal view returns (bool) {
        // Treat both Base mainnet (8453) and Base Sepolia (84532) as Base
        return block.chainid == BASE_CHAIN_ID || block.chainid == 84532;
    }

    /* Views */

    function balanceStored(address wallet, address token) external view returns (uint256) {
        return recipientBalances[token][wallet];
    }

    function balanceReadyToWithdrawal(address wallet, address token) external view returns (uint256) {
        // For now, all stored balance is ready to withdraw
        // This can be extended later to handle pending vs ready states
        return recipientBalances[token][wallet];
    }

    /* TWAP related functions */

    function getMRDNPrice() external view returns (uint256) {
        return _getMRDNPriceFromTWAP();
    }

    function _getMRDNPriceFromTWAP() internal view returns (uint256) {
        if (aerodromePool == address(0)) return 10_000; // Default to 0.01 USD as safety measure (USDC has 6 decimals)

        int24 twapTick = _getTimeWeightedAverageTick(aerodromePool, twapWindow);

        return getQuoteAtTick(twapTick, 1e18, mrdnToken, usdcContract);
    }

    function _getTimeWeightedAverageTick(address pool, uint32 twapInterval) internal view returns (int24) {
        uint32[] memory secondsAgos = new uint32[](2);
        secondsAgos[0] = twapInterval; // from (twapInterval) seconds ago
        secondsAgos[1] = 0; // to now

        (int56[] memory tickCumulatives,) = IAerodromePool(pool).observe(secondsAgos);

        int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
        int56 interval = int56(uint56(twapInterval));
        int56 weightedTick = tickCumulativesDelta / interval;

        if (tickCumulativesDelta < 0 && (tickCumulativesDelta % interval != 0)) {
            // Floor division toward negative infinity to mirror Uniswap conventions
            weightedTick -= 1;
        }

        return int24(weightedTick);
    }

    function getQuoteAtTick(int24 tick, uint256 baseAmount, address baseToken, address quoteToken)
        public
        pure
        returns (uint256 quoteAmount)
    {
        uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);

        if (sqrtRatioX96 <= type(uint128).max) {
            uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96;
            if (baseToken < quoteToken) {
                quoteAmount = mulDiv(ratioX192, baseAmount, uint256(1) << 192);
            } else {
                quoteAmount = mulDiv(uint256(1) << 192, baseAmount, ratioX192);
            }
        } else {
            uint256 ratioX128 = mulDiv(sqrtRatioX96, sqrtRatioX96, uint256(1) << 64);
            if (baseToken < quoteToken) {
                quoteAmount = mulDiv(ratioX128, baseAmount, uint256(1) << 128);
            } else {
                quoteAmount = mulDiv(uint256(1) << 128, baseAmount, ratioX128);
            }
        }
    }

    /* Cashback related functions */

    function getCurrentCashbackRate() external view returns (uint256) {
        if (mrdnToken == address(0)) return 0;
        return _calculateCashbackRate(totalMrdnCashbackDistributed);
    }

    function calculateCashbackAmount(uint256 usdcAmount) external view returns (uint256) {
        if (mrdnToken == address(0)) return 0;

        uint256 mrdnBalance = IERC20(mrdnToken).balanceOf(address(this));
        if (mrdnBalance == 0) return 0;

        uint256 cashbackRate = _calculateCashbackRate(totalMrdnCashbackDistributed);
        uint256 mrdnPriceInUsdc = _getMRDNPriceFromTWAP();
        if (mrdnPriceInUsdc == 0) return 0;

        uint256 cashbackUsdcValue = (usdcAmount * cashbackRate) / BASIS_POINTS_DIVISOR;
        uint256 cashbackMrdnAmount = (cashbackUsdcValue * 1e18) / mrdnPriceInUsdc;

        if (cashbackMrdnAmount > mrdnBalance) {
            cashbackMrdnAmount = mrdnBalance;
        }

        return cashbackMrdnAmount;
    }

    function _calculateAndSendCashback(address recipient, uint256 usdcAmount) internal returns (uint256) {
        if (mrdnToken == address(0)) return 0;

        uint256 mrdnBalance = IERC20(mrdnToken).balanceOf(address(this));
        if (mrdnBalance == 0) return 0;

        // Calculate cashback percentage: y(x) = 0.02 * exp(-6.9e-9 * x)
        // where x is the cumulative MRDN cashback distributed
        uint256 cashbackRate = _calculateCashbackRate(totalMrdnCashbackDistributed);

        // Get MRDN price in USDC from TWAP
        uint256 mrdnPriceInUsdc = _getMRDNPriceFromTWAP();
        if (mrdnPriceInUsdc == 0) return 0;

        // Calculate cashback in USDC value, then convert to MRDN amount
        uint256 cashbackUsdcValue = (usdcAmount * cashbackRate) / BASIS_POINTS_DIVISOR;

        // Convert USDC value to MRDN amount
        uint256 cashbackMrdnAmount = (cashbackUsdcValue * 1e18) / mrdnPriceInUsdc;

        // Cap at available balance
        if (cashbackMrdnAmount > mrdnBalance) {
            cashbackMrdnAmount = mrdnBalance;
        }

        // Send MRDN cashback if amount is greater than 0
        if (cashbackMrdnAmount > 0) {
            totalMrdnCashbackDistributed += cashbackMrdnAmount;
            IERC20(mrdnToken).safeTransfer(recipient, cashbackMrdnAmount);
        }

        return cashbackMrdnAmount;
    }

    function _calculateCashbackRate(uint256 x) internal view returns (uint256) {
        // y(x) = 0.02 * exp(-decayConstant * x)
        // where x is the amount of tokens sent as cashback (capped at CASHBACK_SUPPLY)

        if (x == 0) return 200; // 2% in basis points
        if (x >= CASHBACK_SUPPLY) return 0;

        // Calculate -6.9e-9 * x
        // 6.9e-9 = 6.9 / 1e9 = 6.9 / 1000000000

        // First convert x to UD60x18 (x is already in wei, which is 1e18 units)
        UD60x18 xValue = ud(x);

        // Use the configurable decay constant (already in UD60x18 format)
        // For 6.9e-9: 6.9e-9 * 1e18 = 6.9e9 = 6900000000
        UD60x18 coefficient = ud(decayConstant);

        // Calculate the exponent: 6.9e-9 * x
        // xValue is already in 1e18 scale, coefficient is 6.9e9
        // So we need to divide by 1e18 to get the correct scale
        UD60x18 exponentValue = xValue.mul(coefficient).div(ud(1e18));

        // Calculate exp(-exponent)
        // Since we need exp(-x), and UD60x18 doesn't handle negatives directly,
        // we use exp(-x) = 1 / exp(x)
        UD60x18 expValue;
        if (exponentValue.eq(ZERO)) {
            expValue = ud(1e18);
        } else if (exponentValue.gte(ud(20e18))) {
            // For very large exponents, exp(-x) approaches 0
            return 0;
        } else {
            // Calculate exp(exponent) then take reciprocal
            UD60x18 expPositive = exp(exponentValue);
            expValue = ud(1e18).div(expPositive);
        }

        // Multiply by 0.02 to get the cashback rate
        // 0.02 in UD60x18 = 0.02 * 1e18 = 2e16
        UD60x18 baseRate = ud(2e16);
        UD60x18 cashbackRate = baseRate.mul(expValue);

        // Convert to basis points (multiply by 10000)
        // cashback rate is currently in decimal (e.g., 0.02), need to convert to bps
        // First convert from UD60x18 to uint256, then scale to basis points
        uint256 rateInWei = cashbackRate.unwrap();
        uint256 cashbackBps = (rateInWei * 10000) / 1e18;

        // Cap at 200 basis points (2%)
        if (cashbackBps > 200) {
            cashbackBps = 200;
        }

        return cashbackBps;
    }
}