AcceptQuote is the onchain entrypoint that settles an RFQ trade. As a taker, it’s the only contract message you call. This page documents every field, every encoding rule, and every validation the contract performs – including the features that aren’t obvious from the other docs.
Message shape
MsgExecuteContract transaction to the TrueCurrent contract address, signed by the taker’s private key.
Three encoding rules you must follow
These encoding rules are relatively common to get wrong. However, they are strictly enforced, as the contract parses the message with strict Rustserde types.
1. rfq_id is a JSON number, not a string
The contract field is u64. JSON encoders that stringify large integers will produce bytes the contract rejects with a deserialization error.
Date.now() returns a number – use it directly. In Python, cast int(rfq_id) before serializing; if you build the request from a string, convert first.
2. Quote expiry must be wrapped as {"ts": <ms>}
The contract’s Expiry type is an enum with two variants – timestamp or block height. Serde requires the variant tag:
expiry as a plain integer. You must wrap it before passing to the contract.
3. Quote signature is base64, not hex
Indexers (including TrueCurrent’s) transmit signatures as hex strings ("0xabc123..."). The contract’s signature field is a CosmWasm Binary type, which on the wire is standard base64. You must convert.
Python:
ContractClient.accept_quote() helper in injective-rfq-toolkit handles all three of these automatically. If you’re building from scratch, apply them yourself.
Field reference
Top-level fields:| Field | Type | Required | Description |
|---|---|---|---|
rfq_id | number (u64) | yes | Must match the rfq_id on the request you sent to the indexer. Used as a taker-scoped nonce to prevent replay. |
market_id | string | yes | Injective derivative market hex ID |
direction | string | yes | "long" or "short" – lowercase. This is the taker’s direction. |
margin | string | yes | Taker margin in USDC, decimal string |
quantity | string | yes | Requested quantity |
worst_price | string | yes | Hard price limit. Individual quotes worse than this are rejected. |
quotes | array | yes | One or more maker quotes to consume. |
unfilled_action | null | no | Reserved field; pass null. See Unfilled action below. |
subaccount_nonce | number | null | no | Subaccount index for the taker. Defaults to 0. |
cid | string | null | no | Client identifier echoed in the settlement event, for your own tracking. |
| Field | Type | Required | Description |
|---|---|---|---|
maker | string | yes | Maker’s inj1... address |
margin | string | yes | Maker’s margin commitment |
quantity | string | yes | Quantity the maker is offering |
price | string | yes | Maker’s quoted price |
expiry | {ts: number} or {h: number} | yes | Quote expiry – wrapped enum |
signature | string (base64) | yes | Maker’s signature over the canonical quote payload |
nonce | number | null | no | Reserved for non-standard quote paths; normal RFQ quotes omit it |
min_fill_quantity | string | null | no | Maker-imposed minimum fill; if the contract would fill them for less, the quote is skipped |
Single-quote settlement
The simplest case: you collect quotes, pick one, accept it. Python – high-level helper:Multi-quote aggregation
Thequotes field is an array. You can submit multiple quotes from different makers in a single AcceptQuote call, and the contract will consume them sequentially until your quantity is filled. This is the main mechanism for getting size done when no single maker can cover your whole request.
Scenario: you want to go long 100 INJ. Three makers respond:
| Maker | Quantity | Price |
|---|---|---|
| Alice | 40 | 4.90 |
| Bob | 40 | 4.92 |
| Carol | 50 | 4.95 |
- Fill 40 from Alice at 4.90 → remaining 60
- Fill 40 from Bob at 4.92 → remaining 20
- Fill 20 from Carol at 4.95 → remaining 0 (partial consumption of Carol’s quote – legal and expected)
- Done. Your position opens with a volume-weighted entry.
Important: quote order matters. The contract processes the quotes array in submission order, not by price. If you submit Carol first, the contract happily takes 50 at 4.95, then Bob’s 40 at 4.92, then only 10 from Alice – you end up with a worse blended price.
Therefore, always sort your quotes by price before submitting. For long sort quotes by ascending price. For shorts, sort quotes by descending price.
There is a maximum. The contract enforcesquotes.len() <= config.max_quotes. In practice this is approximately 20. Check the current value with aconfigquery to obtain the exact number before submitting very long lists.
Unfilled action
quantity, the quote still settles the portion that was filled — the remainder is simply not traded. If zero quotes fill (e.g. all were rejected by signature or expiry), the quote fails.
Pass null for unfilled_action on every call. The contract field exists for future use; non-null values are not exposed in the public product today.
On-chain validation
For each quote in the submitted array, the contract performs the following checks in order. If a quote fails any check, it is skipped (with an entry inquote_results), and the loop continues to the next quote.
| Check | Failure mode |
|---|---|
Quote expiry not passed | Skip with “quote expired” |
| Maker is currently whitelisted | Skip with “unknown maker” |
Maker has not already used this (taker, rfq_id) nonce | Skip with “nonce replay” |
| Signature verifies against canonical quote payload | Skip with “signature mismatch” |
Quote price is within taker’s worst_price | Skip with “price exceeds worst_price” |
| Maker has sufficient available balance for their margin | Skip with “insufficient maker balance” |
Filling this quote wouldn’t fall below maker’s min_fill_quantity | Skip with “below min fill” |
| Quote direction matches (implicit from signature) | Skip with “direction mismatch” |
- At least some fills happened – if
filled_quantity == 0, the whole quote fails withall quotes rejected. If at least one quote filled, the quote settles the filled quantity and the remainder is simply not traded. - Taker has enough balance for the aggregate margin used across all filled quotes.
quotes.len() <= config.max_quotes– checked at the top of the handler before any iteration.
quote_results, keyed by maker address. Inspect this list in a failed quote to diagnose exactly which quote was rejected and why.
Post-settlement
After a successful quote:- You have a new position in your Injective exchange subaccount. Query it via the standard exchange module APIs – there is no RFQ-specific position state onchain.
- The settlement event contains the filled quantity, the per-quote
quote_results, the taker’s aggregate entry price, and thecidyou passed (useful for matching events to your trade log). - Fees have been deducted according to the current
taker_fee_rateandmaker_fee_ratein the contract’s config. Queryconfigfor the current values.
Querying the settlement history
The indexer maintains a history of settlements indexed by taker address. Query it via HTTP:rfq_id, tx_hash, fill quantities, per-quote results, and timestamps. Use this to reconcile your client-side trade log with onchain reality.
Next
- Best practices – slippage strategy, expiry races, idempotency
- TakerStream – collecting quotes to feed into
AcceptQuote - Signed taker intents - the conditional/TP-SL path (pre-signed trigger-gated intents executed by the TP/SL executor)
- Authorization setup – the grants you need before any of this works

