Order Types
In our JSON‑RPC API, each order is submitted as a payload conforming to the Order
enum. Rather than using a generic order with optional fields, each payload is a specific variant containing exactly the data needed for that order type. This approach minimizes ambiguity and simplifies validation, allowing a single API endpoint to handle all orders seamlessly.
At a high level, orders are grouped into three categories:
Flash Orders – For single‑block execution.
• Partial Flash Orders allow partial fills (similar to an Immediate‑or‑Cancel order).
• Exact Flash Orders require complete fill (similar to a Fill‑or‑Kill order).
Standing Orders – Persist until filled, canceled, or expired (akin to Good‑Til‑Canceled orders) and include replay protection via a nonce and deadline.
• Partial Standing Orders permit partial fills until their specified expiry block.
• Exact Standing Orders require complete execution.
TOB Orders – Unique to Angstrom’s arbitrage auction. These orders allow arbitrageurs to bid for the right to execute the first trade against the liquidity pool.
In Rust, the order enum is defined as follows:
Order Enum– Rust Type
pub enum Order {
PartialFlash(PartialFlashOrder),
ExactFlash(ExactFlashOrder),
PartialStanding(PartialStandingOrder),
ExactStanding(ExactStandingOrder),
TOB(TopOfBlockOrder),
}
This design ensures that the API endpoint receives a self-contained payload, unambiguously specifying the order type and its required fields.
Flash Orders
Flash orders execute within a single block. They come in two forms:
Partial Flash Orders
A Partial Flash Order specifies a range of acceptable input amounts (min_amount_in
and max_amount_in
) with a minimum acceptable price (min_price
). This order will execute partially if at least min_amount_in
is filled; otherwise, it expires immediately similar to an Immediate-or-Cancel (IOC) order.
Partial Flash Order (JSON)
{
"PartialFlash": {
"ref_id": 0, // Integrator reward ID
"min_amount_in": 1679691276497, // Minimum amount to be filled
"max_amount_in": 8398456393500, // Maximum amount to be filled
"max_extra_fee_asset0": 76143570, // Max gas fee paid in asset0
"min_price": "0x99972442831653c2f9a", // Minimum price in RAY (1e27)
"use_internal": false, // Toggle for internal funds
"asset_in": "0x...", // Input asset address
"asset_out": "0x...", // Output asset address
"recipient": "0x...", // Recipient address (if zero, defaults to signer).
"hook_data": "0x", // Reserved for future use, must be empty bytes
"valid_for_block": 21946390, // Block number when the order is valid
"meta": {
"isEcdsa": true, // True if EIP 712, false if EIP 1271
"from": "0x...", // Signer address
"signature": "0x..." // Signature in bytes
}
}
}
Partial Flash Order – Rust Type
pub struct PartialFlashOrder {
pub ref_id: u32,
pub min_amount_in: u128,
pub max_amount_in: u128,
pub max_extra_fee_asset0: u128,
pub min_price: u256,
pub use_internal: bool,
pub asset_in: address,
pub asset_out: address,
pub recipient: address,
pub hook_data: bytes,
pub valid_for_block: u64,
pub meta: OrderMeta,
}
Exact Flash Orders
An Exact Flash Order specifies either an exact input (exact_in: true
) or an exact output amount (exact_in: false
), alongside a minimum acceptable price (min_price
).
If the order cannot be completely filled at the specified conditions immediately within the block, it expires automatically without execution. This behavior closely resembles a Fill-or-Kill (FOK) order
Exact Flash Order (JSON)
{
"ExactFlash": {
"ref_id": 0,
"exact_in": true, // true for exact-in, false for exact-out.
"amount": 1234567890, // Fixed order amount.
"max_extra_fee_asset0": 76143570,
"min_price": "0x99972442831653c2f9a",
"use_internal": false,
"asset_in": "0x...",
"asset_out": "0x...",
"recipient": "0x...",
"hook_data": "0x",
"valid_for_block": 21946390,
"meta": {
"isEcdsa": true,
"from": "0x...",
"signature": "0x..."
}
}
}
Exact Flash Order – Rust Type
pub struct ExactFlashOrder {
pub ref_id: u32,
pub exact_in: bool,
pub amount: u128,
pub max_extra_fee_asset0: u128,
pub min_price: u256,
pub use_internal: bool,
pub asset_in: address,
pub asset_out: address,
pub recipient: address,
pub hook_data: bytes,
pub valid_for_block: u64,
pub meta: OrderMeta,
}
Standing Orders
Standing orders persist over multiple blocks until canceled or expired. They include additional fields (nonce and deadline) to prevent replay attacks.
Note: Angstrom uses unique unordered nonces. A nonce is valid only once, and the nonces do not need to be sequential.
Partial Standing Orders
A Partial Standing Order specifies a range of acceptable input amounts (min_amount_in
, max_amount_in
) and a minimum price (min_price
). It is valid until first filled (even partially), canceled, or expired.
Note: Once the order is filled—even partially—it is immediately considered complete and removed from the order book, regardless of whether the maximum amount has been fully executed.
Partial Standing Order (JSON)
{
"PartialStanding": {
"ref_id": 0,
"min_amount_in": 1679691276497,
"max_amount_in": 8398456393500,
"max_extra_fee_asset0": 76143570,
"min_price": "0x...",
"use_internal": false,
"asset_in": "0x...",
"asset_out": "0x...",
"recipient": "0x...",
"hook_data": "0x",
"nonce": 1,
"deadline": 1234567890,
"meta": {
"isEcdsa": true,
"from": "0x...",
"signature": "0x..."
}
}
}
Partial Standing Order – Rust Type
pub struct PartialStandingOrder {
pub ref_id: u32,
pub min_amount_in: u128,
pub max_amount_in: u128,
pub max_extra_fee_asset0: u128,
pub min_price: u256,
pub use_internal: bool,
pub asset_in: address,
pub asset_out: address,
pub recipient: address,
pub hook_data: bytes,
pub nonce: u64,
pub deadline: u40,
pub meta: OrderMeta,
}
Exact Standing Orders
An Exact Standing Order specifies an exact input (exact_in: true
) or exact output amount (exact_in: false
) alongside a minimum acceptable price (min_price
). These orders remain active until filled, canceled, or expired at their deadline, similar to traditional Good-'Til-Canceled (GTC) orders.
Exact Standing Order (JSON)
{
"ExactStanding": {
"ref_id": 0,
"exact_in": true,
"amount": 1234567890,
"max_extra_fee_asset0": 76143570,
"min_price": "0x...",
"use_internal": false,
"asset_in": "0x...",
"asset_out": "0x...",
"recipient": "0x...",
"hook_data": "0x",
"nonce": 1,
"deadline": 1234567890,
"meta": {
"isEcdsa": true,
"from": "0x...",
"signature": "0x..."
}
}
}
Exact Standing Order– Rust Type
pub struct ExactStandingOrder {
pub ref_id: u32,
pub exact_in: bool,
pub amount: u128,
pub max_extra_fee_asset0: u128,
pub min_price: u256,
pub use_internal: bool,
pub asset_in: address,
pub asset_out: address,
pub recipient: address,
pub hook_data: bytes,
pub nonce: u64,
pub deadline: u40,
pub meta: OrderMeta,
}
TOB Orders
TOB (Top‑of‑Block) orders are unique to Angstrom’s arbitrage auction. They enable arbitrageurs to bid for right to execute the first (fee-free) trade against the liquidity pool at the start of a block. In these orders, the auction bid is always denominated in Token0 as we always donate the bid to LPs in Token0 to save gas.
The Token order are determined by their hexadecimal values: the token with the lower value is Token0 and the one with the higher value is Token1.
For ask orders (Token0 in, Token1 out), the order specifies an input amount in T0 and an expected output in T1. The auction bid is calculated as the extra T0 provided beyond the amount needed (as determined by the AMM’s quote) to reach the desired T1 output.
- Example: If the AMM would normally swap 10 T0 for 5 T1, but an order specifies
quantity_in
12 (T0) forquantity_out
5 (T1) out, the effective auction bid is 2 T0.
For bid orders (Token1 in, Token0 out), the order specifies an input in T1 and a reduced output in T0. The shortfall in T0 relative to the AMM’s expected output represents the auction bid.
- Example: If the AMM would swap 10 T1 for 5T0, but the order specifies
quantity_in
10 (T1) forquantity_out
3 (T0) the auction bid is 2 T0.
This implicit bid mechanism means that no additional flag is needed—the auction bid is derived solely from the difference between the AMM’s deterministic quote and the order’s specified amounts, with all bid values expressed in Token0.
Note: If this seems overly complex, don’t worry. When using our SDK, these details are fully abstracted away, so you can integrate without needing to manage the underlying intricacies.
JSON Representation
{
"TOB": {
"quantity_in": 1234567890,
"quantity_out": 987654321,
"max_gas_asset0": 76143570,
"use_internal": false,
"asset_in": "0x...",
"asset_out": "0x...",
"recipient": "0x...",
"valid_for_block": 21946390,
"meta": {
"isEcdsa": true,
"from": "0x...",
"signature": "0x..."
}
}
}
Underlying Rust Definition
TopOfBlockOrder (Rust)
pub struct TopOfBlockOrder {
pub quantity_in: u128,
pub quantity_out: u128,
pub max_gas_asset0: u128,
pub use_internal: bool,
pub asset_in: address,
pub asset_out: address,
pub recipient: address,
pub valid_for_block: u64,
pub meta: OrderMeta,
}
Order Metadata
All order variants include common metadata for signature verification.
JSON Representation
{
"meta": {
"isEcdsa": true,
"from": "0x...",
"signature": "0x..."
}
}
Underlying Rust Definition
OrderMeta (Rust)
pub struct OrderMeta {
pub isEcdsa: bool, // True if EIP 712, false if EIP 1271
pub from: address, // Signer address
pub signature: bytes, // Signature in bytes
}