Skip to main content
Every maker quote is an EIP-712 v2 secp256k1 signature over the SignQuote typed-data payload. The contract verifies that signature during settlement.
This guide is EIP-712 v2 only. Quote payloads must include sign_mode: "v2" and evm_chain_id. Do not sign JSON strings, eth_sign messages, or generic wallet typed-data payloads unless you have reproduced the exact digest used by the protocol.
There are two different chain IDs in every v2 quote. EIP-712 uses a field named chainId, but that value is the numeric EVM chain ID: 1439 on testnet or 1776 on mainnet. The MakerStream quote payload uses snake_case: chain_id is the Cosmos chain ID, and evm_chain_id is the numeric EVM chain ID. Passing 1439 or 1776 as quote.chain_id is invalid.

1. Decide whether to quote

For each request, decide:
  • whether your maker is willing to fill it,
  • how much quantity and margin to provide,
  • what price to quote,
  • how long the quote remains valid,
  • and which maker subaccount nonce backs the quote.
Direction is from the taker’s perspective:
Taker directionMaker exposure
longMaker fills the short side
shortMaker fills the long side
Your quoted margin and quantity may be smaller than the taker’s request for partial fills.

2. Canonicalize decimals

Decimal fields are signed as exact UTF-8 strings. These are different signed messages:
  • "14.85"
  • "14.850"
  • "14.8500"
Apply one canonicalization helper to every decimal field before signing and sending: price, taker margin, taker quantity, maker margin, maker quantity, minimum fill quantity, trigger price, and worst price.
from decimal import Decimal, ROUND_DOWN

def to_canonical(x, tick) -> str:
    """Quantize x to the market tick and emit a plain decimal string."""
    return format(
        Decimal(str(x)).quantize(
            Decimal(str(tick)),
            rounding=ROUND_DOWN,
        ).normalize(),
        "f",
    )

# "4.50"    -> "4.5"
# "76462.0" -> "76462"   # integer-tick market
# "110.00"  -> "110"
Never send locale commas, scientific notation, 1e18-scaled integers, or values with trailing zeros after quantization.

3. EIP-712 domain

Quotes use this EIP-712 domain:
FieldValue
name"RFQ"
version"1"
chainId testnet1439
chainId mainnet1776
verifyingContractEVM address derived from the RFQ contract bech32 address
The domain chainId is the EVM chain ID. It is not the Cosmos chain ID. This is the main naming collision in RFQ: chainId in the EIP-712 domain is not the same field as quote.chain_id in the MakerStream payload.
ContextTestnetMainnet
EIP-712 domain chainId14391776
Quote payload evm_chain_id14391776
Quote payload chain_idinjective-888injective-1
Current testnet RFQ contract:
inj1qw7jk82hjvf79tnjykux6zacuh9gl0z0wl3ruk

4. SignQuote typed-data layout

The SignQuote message has 16 fields. Field order matters.
#FieldTypeEncoding
1evmChainIduint64Same EVM chain ID used in the domain and quote evm_chain_id
2marketIdstringkeccak256(utf8(s))
3rfqIduint64Big-endian integer word
4takeraddress20-byte EVM address from taker inj1...
5takerDirectionuint80 for long, 1 for short
6takerMarginstringkeccak256(utf8(s))
7takerQuantitystringkeccak256(utf8(s))
8makeraddress20-byte EVM address from maker inj1...
9makerSubaccountNonceuint32Big-endian integer word
10makerQuantitystringkeccak256(utf8(s))
11makerMarginstringkeccak256(utf8(s))
12pricestringkeccak256(utf8(s)); must be tick-quantized
13expiryKinduint80 for timestamp ms, 1 for block height
14expiryValueuint64Timestamp ms or block height
15minFillQuantitystringUse "0" when absent; never empty string
16bindingKinduint81 for current taker-bound RFQ quotes
The final digest is:
keccak256(0x19 || 0x01 || domainSeparator || keccak256(typeHash || encoded SignQuote fields))
The helper derives bindingKind from whether taker is present. For current maker integrations, pass the taker address from the RFQ request. Do not add bindingKind to the quote payload, and do not pass a binding-kind helper argument for live taker-bound RFQs.

5. Sign with the helper

Use sign_quote_v2 from injective-rfq-toolkit.
import time
from rfq_test.crypto.eip712 import sign_quote_v2

expiry_ms = int(time.time() * 1000) + 2_000  # must be at least now + 1500ms

sig = sign_quote_v2(
    private_key=MM_PRIVATE_KEY,
    evm_chain_id=1439,
    verifying_contract_bech32="inj1qw7jk82hjvf79tnjykux6zacuh9gl0z0wl3ruk",
    market_id=MARKET.id,
    rfq_id=int(rfq_id),
    taker=taker_addr,
    direction="long",
    taker_margin="100",
    taker_quantity="10",
    maker=mm_addr,
    maker_subaccount_nonce=0,
    maker_margin="100",
    maker_quantity="10",
    price="14.85",
    expiry_ms=expiry_ms,
    min_fill_quantity=None,
)
The helper returns an 0x-prefixed 65-byte signature (r || s || v) with compact y-parity v=0/1.

6. Send the same values

Send the exact strings and numbers you signed.
ack = await mm_ws.send_quote({
    "chain_id": "injective-888",
    "contract_address": "inj1qw7jk82hjvf79tnjykux6zacuh9gl0z0wl3ruk",
    "rfq_id": int(rfq_id),
    "market_id": MARKET.id,
    "taker_direction": "long",
    "margin": "100",
    "quantity": "10",
    "price": "14.85",
    "expiry": expiry_ms,
    "maker": mm_addr,
    "maker_subaccount_nonce": 0,
    "taker": taker_addr,
    "signature": sig,
    "sign_mode": "v2",
    "evm_chain_id": 1439,
}, wait_for_response=True, response_timeout=5.0)
chain_id is still the Cosmos chain ID. evm_chain_id is the EVM chain ID embedded in the EIP-712 domain. Do not send 1439 or 1776 in chain_id.

Common failures

SymptomFix
sign_mode required or empty signing modeInclude "sign_mode": "v2" on every quote payload
Wrong chain IDUse 1439 / 1776 in EIP-712 domain chainId and quote evm_chain_id; use injective-888 / injective-1 in quote chain_id
invalid_signatureCheck field order, EVM-form verifying contract, compact v=0/1, exact decimal strings, and maker_subaccount_nonce
Non-canonical priceStrip trailing zeros after quantizing; send "76462" for integer-tick markets, not "76462.0"
Digest mismatch on minimum fillUse None in the helper or "0" in the signed field; never ""
Quote accepted by indexer but skipped onchainCheck expiry, worst_price, mark-band validation, maker registration, and maker margin
Next: Sending quotes.
Last modified on May 29, 2026