Transaction Fields
Every transaction submitted to the Bullet exchange is wrapped in an UnsignedTransaction containing the call message,
a uniqueness value, transaction details, and an optional address_override. This guide covers the uniqueness,
details, and address override fields.
Uniqueness
The UniquenessData field prevents transaction replay. Bullet supports three modes:
#![allow(unused)]
fn main() {
enum UniquenessData {
Nonce(u64) = 0,
Generation(u64) = 1,
Window(u64) = 2,
}
}
Window mode (recommended)
The uniqueness value can be whatever you want — it just needs to be unique per key within an increasing block window of 1000 nonces. We recommend using millisecond timestamps.
Compared to the strictly monotonic Nonce variant multiple transactions can be in-flight concurrently.
Best practice: use SystemTime::now() in milliseconds. Avoid reusing values — if you submit multiple
transactions in a tight loop, ensure each is distinct.
Nonce mode
Use Nonce(u64) for strict sequential ordering. The nonce must be exactly current_nonce + 1 for the credential.
This mode is simpler but only allows one in-flight transaction at a time.
TxDetails
#![allow(unused)]
fn main() {
struct TxDetails {
max_priority_fee_bips: u64,
max_fee: Amount,
gas_limit: Option<[u64; 2]>,
chain_hash_fragment: u64,
}
}
These fields are required in every transaction. We recommend using the values below.
| Field | Type | Description |
|---|---|---|
chain_hash_fragment | u64 | First 8 bytes of the chain hash (GET /fapi/v1/exchangeInfo → chainHash) as a little-endian u64; see chain_hash_fragment |
max_fee | Amount(u128) | Maximum fee the sender is willing to pay |
gas_limit | Option<[u64; 2]> | 2D gas limit: [compute, storage]. None defaults to block limit |
max_priority_fee_bips | u64 | Priority fee tip in basis points |
The fragment lets the exchange pick the chain hash a transaction was signed over when more than one is valid
during a chain-hash rotation. It replaces the former chain_id field.
Gas model
Bullet uses a 2D gas model with separate compute and storage dimensions. The gas_limit field caps how much of
each resource a transaction may consume.
Paymaster
Gas fees are covered by a paymaster — users do not pay gas out of pocket. The fields are still required for transaction validity.
Recommended Values
| Field | Recommended value | Notes |
|---|---|---|
uniqueness | Window(timestamp_ms()) | Millisecond timestamp |
max_priority_fee_bips | 0 | |
max_fee | 1 << 48 | |
gas_limit | None | Defaults to block limit. Use [5_000_000, 5_000_000] for order placement or [500_000, 500_000] for simpler ops if you want explicit caps |
chain_hash_fragment | chain_hash_fragment(&chain_hash) | Derived from the chainHash you sign with |
address_override | None | Execute as your own address. Only set it to act on another account that explicitly authorized your key |
See the default_tx_details helper for a ready-to-use implementation.
Address override
UnsignedTransaction.address_override is an Option<[u8; 32]> and is part of the signed bytes. Leave it None:
the transaction then executes as the account the exchange resolves for the signing credential, which is the
key’s own account or, for a registered delegate key, the delegator’s main account (see
Delegate Accounts; delegation needs no override). Some(address) executes as that
account instead and is only admitted when the account has explicitly authorized the signing credential;
otherwise the transaction is skipped before execution.