RfqExecutor

0xf4da3c42d9c9f494d4688a0112a9db3c9b18a914

Verification
Verified
v0.8.27+commit.40a35a09
Type
Contract
2,688 bytes
ABI entries
13
5 read · 1 write
License
none

Contract information

Address
0xf4da3c42d9c9f494d4688a0112a9db3c9b18a914
Chain
Robinhood Chain (4663)
Compiler
v0.8.27+commit.40a35a09
Optimization
Enabled
Creation tx
0xdcae0cf18d…8b3fb69409

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (5)

ARCUS_RFQ_QUOTE_TYPEHASH()bytes32
ARCUS_RFQ_QUOTE_WITNESS_TYPE_STRING()string
PERMIT2()address
hashArcusRfqQuote(tuple)bytes32
settlement()address

Events (1)

ArcusRfqFilled

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "settlement_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "OnlySettlement",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "QuoteExpired",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "QuoteMismatch",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      }
    ],
    "name": "SafeERC20FailedOperation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "WrappedQuoteUnsupported",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "quoteId",
        "type": "bytes32"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "maker",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "taker",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "makerBuyToken",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "address",
        "name": "makerSellToken",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "makerBuyAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "makerSellAmount",
        "type": "uint256"
      }
    ],
    "name": "ArcusRfqFilled",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "ARCUS_RFQ_QUOTE_TYPEHASH",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "ARCUS_RFQ_QUOTE_WITNESS_TYPE_STRING",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "PERMIT2",
    "outputs": [
      {
        "internalType": "contract IPermit2",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "taker",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "inputToken",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "inputAmount",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "outputToken",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "minOutputAmount",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "executorData",
        "type": "bytes"
      }
    ],
    "name": "execute",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "outputAmount",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "bytes32",
            "name": "quoteId",
            "type": "bytes32"
          },
          {
            "internalType": "address",
            "name": "maker",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "taker",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "makerBuyToken",
            "type": "address"
          },
          {
            "internalType": "address",
            "name": "makerSellToken",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "makerBuyAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "makerSellAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "nonce",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "validUntil",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "wrapped",
            "type": "bool"
          }
        ],
        "internalType": "struct ArcusRfqQuote",
        "name": "quote",
        "type": "tuple"
      }
    ],
    "name": "hashArcusRfqQuote",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "settlement",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IArcusExecutor} from "../interfaces/IArcusExecutor.sol";
import {IPermit2} from "../interfaces/IPermit2.sol";
import {ArcusRfqQuote, ArcusRfqQuoteLib} from "../lib/ArcusRfqQuoteLib.sol";

/// @notice Ordinary RFQ executor for fixed-size maker quotes.
contract RfqExecutor is IArcusExecutor {
    using SafeERC20 for IERC20;

    IPermit2 public constant PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);

    string public constant ARCUS_RFQ_QUOTE_WITNESS_TYPE_STRING =
        "ArcusRfqQuote witness)ArcusRfqQuote(bytes32 quoteId,address maker,address taker,address makerBuyToken,address makerSellToken,uint256 makerBuyAmount,uint256 makerSellAmount,uint256 nonce,uint256 validUntil,bool wrapped)TokenPermissions(address token,uint256 amount)";

    address public immutable settlement;

    event ArcusRfqFilled(
        bytes32 indexed quoteId,
        address indexed maker,
        address indexed taker,
        address makerBuyToken,
        address makerSellToken,
        uint256 makerBuyAmount,
        uint256 makerSellAmount
    );

    error OnlySettlement();
    error QuoteExpired();
    error WrappedQuoteUnsupported();
    error QuoteMismatch();

    constructor(address settlement_) {
        settlement = settlement_;
    }

    function execute(
        address taker,
        address inputToken,
        uint256 inputAmount,
        address outputToken,
        uint256 minOutputAmount,
        bytes calldata executorData
    ) external override returns (uint256 outputAmount) {
        if (msg.sender != settlement) revert OnlySettlement();

        (ArcusRfqQuote memory quote, bytes memory makerSig) = abi.decode(executorData, (ArcusRfqQuote, bytes));
        if (quote.wrapped) revert WrappedQuoteUnsupported();
        if (block.timestamp > quote.validUntil) revert QuoteExpired();
        if (
            quote.taker != taker || inputToken != quote.makerBuyToken || inputAmount != quote.makerBuyAmount
                || outputToken != quote.makerSellToken || quote.makerBuyAmount == 0 || quote.makerSellAmount == 0
                || quote.makerSellAmount < minOutputAmount
        ) revert QuoteMismatch();

        PERMIT2.permitWitnessTransferFrom(
            IPermit2.PermitTransferFrom({
                permitted: IPermit2.TokenPermissions({token: quote.makerSellToken, amount: quote.makerSellAmount}),
                nonce: quote.nonce,
                deadline: quote.validUntil
            }),
            IPermit2.SignatureTransferDetails({to: address(this), requestedAmount: quote.makerSellAmount}),
            quote.maker,
            _hashArcusRfqQuote(quote),
            ARCUS_RFQ_QUOTE_WITNESS_TYPE_STRING,
            makerSig
        );

        IERC20(quote.makerBuyToken).safeTransfer(quote.maker, quote.makerBuyAmount);
        IERC20(quote.makerSellToken).safeTransfer(msg.sender, quote.makerSellAmount);

        emit ArcusRfqFilled(
            quote.quoteId,
            quote.maker,
            quote.taker,
            quote.makerBuyToken,
            quote.makerSellToken,
            quote.makerBuyAmount,
            quote.makerSellAmount
        );

        return quote.makerSellAmount;
    }

    function hashArcusRfqQuote(ArcusRfqQuote calldata quote) external pure returns (bytes32) {
        return _hashArcusRfqQuote(quote);
    }

    function ARCUS_RFQ_QUOTE_TYPEHASH() external pure returns (bytes32) {
        return ArcusRfqQuoteLib.typehash();
    }

    function _hashArcusRfqQuote(ArcusRfqQuote memory quote) internal pure returns (bytes32) {
        return ArcusRfqQuoteLib.hash(quote);
    }
}
Chain explorer3112msChain node93ms