Skip to main content
A taker_quote response must include a signature field: the maker signs the filled quote inline. See Quotes › Responding for the response shape. This page covers the EIP-712 order types, the sign schemes Bebop accepts, and a full Python signing example.
RFQA order signing is unchanged from what’s described here. If you attach maker hooks, each hook needs a separate EIP-712 signature over the BebopHook struct, using the BebopRouter domain rather than BebopSettlement.

Order types

Use SingleOrder for single-pair, single-maker swaps. Use MultiOrder whenever Bebop’s router involves multiple makers or multiple legs on your side: middle-token routing, and 12M / M21 multi-token trades. The order_signing_type property on the incoming quote indicates which type to use.
  • SingleOrder: single pair, single maker
    struct SingleOrder {
        uint64 partner_id;
        uint256 expiry;
        address taker_address;
        address maker_address;
        uint256 maker_nonce;
        address taker_token;
        address maker_token;
        uint256 taker_amount;
        uint256 maker_amount;
        address receiver;
        uint256 packed_commands;
    }
    
  • MultiOrder: multi-maker, and/or multiple legs on your side (middle-token or 12M/M21)
    struct MultiOrder {
        uint64 partner_id;
        uint256 expiry;
        address taker_address;
        address maker_address;
        uint256 maker_nonce;
        address[] taker_tokens;
        address[] maker_tokens;
        uint256[] taker_amounts;
        uint256[] maker_amounts;
        address receiver;
        bytes commands;
    }
    

Middle-token routing

A quote with order_type: 121 can arrive with onchain_order_type: MultiOrder when the router uses middle-token routing (passing through an intermediate token to get a better price). This is the most common reason a signature that worked in testing fails in production:
  • Multi-maker middle-token routing can’t be reproduced with only your own maker in the loop.
  • /maker-status includes a hardcoded middle-token test case. When the signature test fails, the response includes the expected signable_message so you can compare against yours byte-for-byte.
  • Bebop runs random signature spot checks on production quotes.
The signing example below is a starting point. For multi-leg cases, treat /maker-status as the canonical reference.

Sign schemes

Return a sign_scheme alongside the signature bytes in your taker_quote response:
  • EIP712: EOA signing. Use this for a standard externally-owned maker address.
  • EIP1271: contract-wallet signing. Bebop verifies the signature on-chain by calling isValidSignature on the maker contract.

Signing example

Construct the partial message and sign with EIP-712.
quote = <response given to quote request>

# use the on-chain arrays from the quote request, do NOT derive them
# by deduplicating `quotes` by token address. 
# when an order routes through a middle token, the same address can appear 
# in several slots of taker_tokens / maker_tokens on purpose, and collapsing
# those slots yields a different EIP-712 hash and therefore an invalid signature.
taker_tokens = quote["taker_tokens"]
maker_tokens = quote["maker_tokens"]

# sum each partial's amount into the slot pointed to by *_tokens_indices.
taker_amounts = [0] * len(taker_tokens)
maker_amounts = [0] * len(maker_tokens)
for partial, t_idx, m_idx in zip(
    quote["quotes"],
    quote["taker_tokens_indices"],
    quote["maker_tokens_indices"],
    strict=True,
):
    taker_amounts[t_idx] += int(partial["taker_amount"])
    maker_amounts[m_idx] += int(partial["maker_amount"])

to_sign = {
    "partner_id": quote["onchain_partner_id"],
    "expiry": quote["expiry"],
    "taker_address": quote["taker_address"],
    "maker_address": quote["maker_address"],
    "maker_nonce": quote["maker_nonce"],
    "receiver": quote["receiver"],
}

if quote["order_signing_type"] == "MultiOrder":
    to_sign["commands"] = AsyncWeb3.to_bytes(hexstr=quote["commands"])
    to_sign["taker_tokens"] = taker_tokens
    to_sign["maker_tokens"] = maker_tokens
    to_sign["taker_amounts"] = taker_amounts
    to_sign["maker_amounts"] = maker_amounts
    msg_order = [
        {"name": "partner_id", "type": "uint64"},
        {"name": "expiry", "type": "uint256"},
        {"name": "taker_address", "type": "address"},
        {"name": "maker_address", "type": "address"},
        {"name": "maker_nonce", "type": "uint256"},
        {"name": "taker_tokens", "type": "address[]"},
        {"name": "maker_tokens", "type": "address[]"},
        {"name": "taker_amounts", "type": "uint256[]"},
        {"name": "maker_amounts", "type": "uint256[]"},
        {"name": "receiver", "type": "address"},
        {"name": "commands", "type": "bytes"},
    ]
else:
    to_sign["packed_commands"] = quote["packed_commands"]
    to_sign["taker_token"] = taker_tokens[0]
    to_sign["maker_token"] = maker_tokens[0]
    to_sign["taker_amount"] = taker_amounts[0]
    to_sign["maker_amount"] = maker_amounts[0]
    msg_order = [
        {"name": "partner_id", "type": "uint64"},
        {"name": "expiry", "type": "uint256"},
        {"name": "taker_address", "type": "address"},
        {"name": "maker_address", "type": "address"},
        {"name": "maker_nonce", "type": "uint256"},
        {"name": "taker_token", "type": "address"},
        {"name": "maker_token", "type": "address"},
        {"name": "taker_amount", "type": "uint256"},
        {"name": "maker_amount", "type": "uint256"},
        {"name": "receiver", "type": "address"},
        {"name": "packed_commands", "type": "uint256"},
    ]

msg = {
    "types": {
        "EIP712Domain": [
            {"name": "name", "type": "string"},
            {"name": "version", "type": "string"},
            {"name": "chainId", "type": "uint256"},
            {"name": "verifyingContract", "type": "address"},
        ],
        quote["order_signing_type"]: msg_order,
    },
    "domain": {
        "name": "BebopSettlement",
        "version": "2",
        "chainId": CHAIN_ID,  # as per the quote request msg
        "verifyingContract": BEBOP_CONTRACT_ADDRESS,
    },
    "primaryType": quote["order_signing_type"],
    "message": to_sign,
}

structured_data = encode_structured_data(msg)
account: LocalAccount = Account().from_key(PRIVATE_KEY)
signed_message: SignedMessage = account.sign_message(structured_data)
signature = signed_message.signature.hex()