Rules and Limits
What a Pakt controls and how its exact rules are identified.
A Pakt defines what your agent may do, how much it may spend, and the account conditions required before it acts. You can combine rules to fit your strategy. Every applicable rule must pass before Pakt authorizes an action.
What Rules Check
Rules govern permissions, spending, account conditions, and timing. The tables below cover the supported rule families. Their applicability depends on the venue and action; adding a rule does not enable an unsupported operation. Your draft's readable rule groups explain its limits. The exhaustive ALLOWED / NOT ALLOWED readback is available for a technical audit.
Markets and Permissions
| Rule | What you control |
|---|---|
| Allowed assets | The markets or assets your agent may use |
| Allowed protocols | The venues or protocols through which actions may be routed |
| Allowed action kinds | Which supported actions are permitted, such as trading, withdrawals, or sweeps |
| Allowed order sides | Which buy, sell, or reduce-only order effects are permitted |
| Margin mode | Which margin modes are acceptable for the proposed markets |
Permissions can allow any supported value or only listed values. An empty “only” list allows none. “Any” remains limited to the capabilities of the approved policy program. Omitting a required permission is not equivalent to allowing everything.
Spending, Prices, and Destinations
| Rule | What you control |
|---|---|
| Trade-size cap | Maximum dollar value of an individual trade |
| Notional budget | Maximum combined dollar value of actions checked together |
| Actions per request | Maximum number of actions checked together |
| Price protection | An acceptable price or effective exchange-rate band relative to the authenticated reference price |
| Network-fee cap | Maximum dollar value of the network fee for an action |
| Collateral movement | Maximum collateral outflow in a single action |
| Withdrawal destinations | The addresses to which withdrawals may be sent |
| Bitcoin payout destination | One exact destination for a Bitcoin sweep through BOB Gateway |
| Payments wallet | The wallet a payments Pakt sends from and, per chain, the master it spends from, the gas limit and the fee ceiling |
| Payment tokens and payees | The tokens and payees a services or payments Pakt may pay, each on its own chain, with per-payment ceilings and optional tags |
| Budgets and payee limits | How much a paying services or payments Pakt may send per trailing or UTC calendar window, counted from the account's attested transfer history |
The batch budget and action count apply to one checked group. They do not accumulate across requests and cannot enforce a daily or rolling trading limit. A Hyperliquid request may contain up to ten IOC limit-order legs. Payments do have windowed limits: see Budgets and Payee Limits.
Services and Inference
A services Pakt uses one service-permissions rule with Services and
Inference entries. Each entry commits an exact provider identifier, an
endpoint pattern, an HTTP method, a category, and a per-call USD-micro amount.
Inference entries also commit exact model identifiers; the program checks the
model in the actual JSON request body. Services bodies remain opaque so
providers do not require separate policy implementations.
An endpoint pattern is an absolute https:// URL with an exact host. Each path
segment is a literal, * (exactly one segment), or a final ** (one or more
segments), so https://api.myceliasignal.com/oracle/** covers a whole data
catalogue and https://api.myceliasignal.com/oracle/*/btc/usd covers every BTC
feed. Patterns carry no query string; requests may carry any. Endpoints are
printable ASCII, and a request path with an empty, . or .. segment, a
backslash, a ; path parameter, or any % escape is refused so no server,
proxy or normalizer can decode it into a path the pattern did not cover.
Entries may overlap: a request is allowed when any entry with its provider and
method, a matching pattern and a sufficient cap admits it, so a broad Services
pattern also admits requests a narrower Inference entry would refuse for its
model or cap.
Discovery is an authoring input, not an execution allowance. A newly authored Pakt may use the current catalogue, but its approved provider, endpoint, method, models, funding account, agent, and budget remain fixed until the owner approves a replacement. A request commitment binds these selections, exact body and headers, operation identity, pricing provenance, declared maximum, and expiry.
Without a service-account rule, services execution is application-enforced
and decision-only. Pakt creates an Allow or Refuse proof and receipt; it does not
release a Privy signature, forward the request, pay the provider, or hold the
provider API key in a TEE. The caller must execute the exact approved request and
reuse its operation identity for provider idempotency. The proof is not a
provider-enforced billing ceiling: an inference estimate is not an authenticated
final charge.
Paying for services
A services Pakt that also commits a service-account, payment-assets,
payment-payees and a state-source-config (state source 3, the
service-payment attestor's key, filled in automatically when the draft omits
it; a different key is refused) pays HTTP 402 charges itself: x402 exact and
MPP evm charges in stablecoins on Base, Arbitrum One and Ethereum. It pays
only from the account's payments wallet, a second Privy wallet created in
the dashboard, never from the trading wallet, and the service-account names
that wallet's address.
payment-assets may list only these tokens, each with exactly its pinned
EIP-712 name and version. Any other token, chain or domain text is refused.
Chain (chain_id) | Token | Address | eip712_name | eip712_version |
|---|---|---|---|---|
| Base (8453) | USDC | 0x833589fcd6edb6e08f4c7c32d4f71b54bda02913 | USD Coin | 2 |
| Arbitrum One (42161) | USDC | 0xaf88d065e77c8cc2239327c5edb3a432268e5831 | USD Coin | 2 |
| Arbitrum One (42161) | USDT0 | 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9 | USD₮0 | 1 |
| Ethereum (1) | USDC | 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 | USD Coin | 2 |
| Ethereum (1) | USDT | 0xdac17f958d2ee523a2206206994597c13d831ec7 | "" | "" |
USDT on Ethereum has no EIP-712 domain, so both fields are empty and it can be paid only through Permit2.
The service-account methods are x402 (x402 exact with an EIP-3009
transfer authorization), mpp-evm (MPP evm with an EIP-3009 authorization)
and x402-permit2 (x402 exact with a Permit2 transfer whose spender must be
the x402ExactPermit2Proxy, 0x402085c248EeA27D92E8b30b2C58ed07f9E20001).
Permit2 moves a token only after the payments wallet has approved it, so
x402-permit2 also lets the payments wallet send
approve(Permit2, amount) to a listed token, where Permit2 is
0x000000000022D473030F116dDEE9F6B43aC78BA3. The cycle is enforced exactly:
a nonzero approval only while the token's Permit2 allowance is zero, and a
reset, approve(Permit2, 0), only while it is nonzero. Anything else is
refused before a proof, so the payments wallet never pays gas for a no-op
approval. Each approval uses the payments wallet's next nonce, and its
operation_id names it for the whole account: resending it, even after the
Pakt is replaced, returns the same signed transaction or the same refusal, and
no other approval or service request can reuse it. As for payments, an
enclave refusal to stamp the approval is recorded under its operation_id
with its named reason and is not retried; a new attempt needs a new
operation_id.
The payments wallet pays the gas for that approval itself, so a Pakt with
x402-permit2 must also commit service-network-fees. It caps, per chain,
what one such transaction may cost as gas_limit * max_fee_per_gas in wei:
{"caps":[{"chain_id":1,"max_fee_wei":"2000000000000000"}]} allows at most
0.002 ETH on Ethereum. Each cap must be positive and on a payment chain, and
every chain with a token payable only through Permit2 (USDT on Ethereum) needs
one, or the draft is refused naming the chain. An approval on a chain without
a cap is refused. Its fee per gas must still pay the base fee when it lands:
the attested base fee grown by 12.5 % for every block the chain can add while
the attested block is as old as the attestor allows (24 s on Ethereum, 12 s
elsewhere) plus one, then the priority fee. A cap that cannot pay that is
refused before a proof and recorded nowhere; retry when fees fall. On Base the
cap excludes the separate L1 data fee the OP Stack charges on top of gas,
which it does not bound.
A Permit2 payment whose token the payments wallet has not yet approved (its attested Permit2 allowance is below the payment) is refused before a proof and recorded nowhere, so the same request pays once the approval is mined.
The agent passes the merchant's unchanged challenge (PAYMENT-REQUIRED or
WWW-Authenticate: Payment ...) with the service request, setting
max_charge_usd_micro to the challenge amount. A paying services Pakt rejects
a request without the challenge before proving, so its operation_id stays
free. The program checks:
- the challenge charges for the request's own endpoint: the x402
resource.urlequals the endpoint (ignoring its query), or the MPPrealmis its host; - the method is enabled and the token is one of the pinned 6-decimal USD
stablecoins above, listed in
payment-assetson that chain, under its per-payment ceiling; - the merchant's address on that chain is listed in
payment-payeeswithin its date window, ceiling and optional service pattern, or the unlisted ceiling allows it (0 refuses every unlisted payee); - the attestor-signed facts from one latest block of that chain: the payments wallet's Privy id and address, its token balance, and the token's on-chain domain separator (EIP-3009) or Permit2's domain separator and the token's Permit2 allowance (Permit2);
- the exact Privy signing request for the one
TransferWithAuthorizationor Permit2 transfer the rules allow. Its window (the Permit2deadline) starts at the attestor's signing time and ends at the soonest of the merchant's limit,max_validity_secs, the payee'snot_afterand the Pakt's ownvalid_until. The x402 nonce (also Permit2's) is derived from the request and the MPP nonce is the challenge hash.
When the merchant offers several options, Pakt runs the program's own checks of
endpoint, method, token, payee, amount and window on each one, in the
merchant's order, before attesting anything, and pays the first one
the Pakt admits; pay_with narrows the options to one chain and token. A
Permit2 option is admitted like any other, so list x402-permit2 only when
the payments wallet will approve the token. If no option is admitted, the call
names each option's reason and records nothing.
A window under 60 s, from whichever limit closes first, is likewise refused
before anything runs and again at the attestor's signing time, recording
nothing; so is a request whose expires_at has already passed. If proving
leaves the credential less than a few seconds, or the request expires while it
is proved, nothing is signed or recorded and the same request can be resent;
Pakt attests it again and the window starts again from that time.
An Allow returns the payment header to attach when retrying the request. Pakt
never contacts the merchant. Retrying the same operation_id with the same
challenge returns the same header, so a merchant can settle it at most once. If
the enclave refuses to stamp the payment (for example because another
authorization on the same account stamped first), that refusal is recorded
under the operation_id with its named reason, exactly as a Hyperliquid
signer refusal is; Pakt does not retry it, and a new attempt needs a new
operation_id. Report the settlement transaction hash with
service_reported; it is recorded unverified. A paying services Pakt can be
active beside a Hyperliquid Pakt but not beside a Gateway Pakt: both sign
through one approver whose freshness check Gateway's older, finalized-block
evidence would keep failing. A services Pakt approved before service payments
keeps working decision-only and cannot pay.
Paying on Tempo
A service-account may also enable mpp-tempo-charge and mpp-tempo-session.
These sign Tempo transactions (type 0x76) from the payments wallet in USDC.e on
Tempo (4217); pathUSD and USDC.e on the Moderato testnet (42431) are pinned for
testing. Such a Pakt also needs service-network-fees with a cap for every Tempo
chain it holds a token on. On Tempo the cap is in attodollars, the unit Tempo
prices gas in (10^18 per USD): 50000000000000000 lets one transaction's
gas_limit * max_fee_per_gas reach $0.05. The attestor reads the wallet's Tempo
balance, its protocol nonce, the block's base fee and, for an existing channel,
the channel's deposit and close state.
Every Tempo transaction Pakt signs has exactly one call, uses the account's own
sequential nonce (nonce key 0), and pays its fee in the payment token: twice the
attested base fee, lowered to what the cap allows, and refused when that cannot
pay the base fee. It expires at the merchant's deadline, max_validity_secs or
the payee's not_after, whichever is soonest, and never past the Pakt's
valid_until; the same 60 s minimum window applies as for EVM payments. A charge
must name the request's own host as its realm. The token must cover the amount
plus the most the fee can be.
- Charge. Pass the merchant's
WWW-Authenticatevalue as anmppchallenge. Pakt signstransferWithMemo(recipient, amount, memo), with the memo mppx binds to the challenge, and returns anAuthorization: Paymentheader carrying the signed transaction. The merchant broadcasts it. - Session. Add
tempo_sessionwith the deposit and the address of a session key the agent generates and keeps. Pakt signsopenon the TIP-1034 channel reserve0x4D50500000000000000000000000000000000000, the escrow mppx sessions use, and never sees the key. The channel's payee must be listed inpayment-payees, and its whole deposit counts as one payment to that payee, under the token's and the payee's ceilings. The open credential needs the first voucher, so Pakt returns it as JSON with two placeholders the session key fills. Vouchers and the cooperative close are signed by the session key alone and are bounded by the deposit. A top-up names the channel and adds to its deposit within the same ceilings. - Direct transfer. The
tempo_operationintent signstransfer(to, amount)to a listed payee that names no service, and returns the raw transaction for the agent to broadcast. Itsoperation_idis one operation for the account, like a transfer's: resending it returns the same transaction even after the Pakt is replaced, and other terms under it are refused. - Payer-side close.
request_closeand, after the grace period (900 seconds today),withdrawreturn a channel's deposit to the payments wallet. They are always available under a session-enabled Pakt.
A Tempo challenge that asks to sponsor the fee (feePayer: true) is refused:
sponsorship needs an expiring nonce and a sender signature over a blank fee
token, and Pakt signs only sequential-nonce transactions whose fee the wallet
pays. Budgets across payments are not enforced yet.
Use a unique operation_id per Pakt account and environment. Replacing the Pakt
or changing its funding account, agent or budget does not make that ID fresh:
changed request bytes reject against the original receipt. Check how the provider
scopes its own idempotency keys; some scope them to the account, across agents.
A decision-only services Pakt requires explicit request expiry configuration
and accepts no budgets or payee limits, which count only what a service account
signs. For decision-only requests, host-supplied time, challenge, and request
context are unsigned request evidence, not authenticated provider usage. A
provider's native budget cap is informational to Pakt, not a Pakt-proved
rolling limit or payment control. An optional service_reported completion is
an idempotent, unverified annotation; it does not establish settlement or
change replay eligibility.
Owner review groups the permissions by endpoint host, shows each path with *
and ** marked, and lists each cap and allowed model. Committed values made of
plain printable ASCII are shown as-is; any other value, including one with
spaces, control or bidirectional characters, is shown as a quoted, escaped
literal. Provider, account, agent and budget identifiers and the full rule note
are under Technical details; the exact committed text is under All
committed terms. The same view appears in the activation summary cards.
The inference check binds the single top-level model field, not arbitrary
provider fallback selectors or intermediary method/URL overrides. It has been
checked against a POST /v1/chat/completions primary-model request with only a
content-type header in the proof input. Support for another provider API
requires checking that provider's actual behavior.
An expired durable Allow or reuse of its operation identity with changed bytes is
an invalid_argument, not a retryable infrastructure fault. Inspect the original
receipt and deciding Pakt identified in the error; keep the original identity and
do not create another payment while the provider outcome is uncertain. The
original decision remains durable. Store/prover failures remain internal errors:
inspect history and retry only the original unchanged operation.
Payments from Your Master
A payments Pakt (execution scope payments) lets the agent pay from a
wallet you already hold: your master on Base (8453), Arbitrum (42161) or
Ethereum (1), in USDC, USDT0 on Arbitrum or USDT on Ethereum. It commits exactly
these rules, and no expiry or oracle configuration:
payments-wallet: the address of the account's payments wallet (the attestor resolves its Privy wallet), which sends every transfer, and for each chain the one master it spends from, the transaction gas limit and the most it pays per gas (max_fee_per_gas_wei). A chain without a master cannot be paid on.payment-assets: the same rule services payments use. Each token is bound to its chain with its largest single payment.payment-payees: the same rule services payments use, listed payees only: the unlisted ceiling must be 0 and no entry has a service pattern. A payee is admitted only on its own chain, within its date window and ceiling. The payments wallet is listed as therefillpayee, taggedrefill, on every bound chain by default, up to the largest token ceiling there.state-source-configwith state source 4, the payments attestor's key.- Optionally
payment-budgetsandpayee-recurrence: see Budgets and Payee Limits.
The master approves the payments wallet once per token for a bounded amount.
The Pakt limits what Pakt signs, but the payments wallet is the owner's own
Privy wallet, so its owner login can also spend up to the allowance without the
Pakt: the allowance is the most that can leave the master. A payments Pakt can
be active beside a Hyperliquid or services Pakt but not beside a Gateway Pakt,
for the same approver-freshness reason as paying services. See
Allowances and Payments from Your Master for setting up,
the USDT reset, gas, and revoking with approve(payments wallet, 0).
The agent proposes a transfer with an operation_id, chain, token, amount and
one of: a payee address, a payee_label from payment-payees (such as
refill), or return_to_master. Against the attestor's signed facts for that
chain (the master's balance and allowance, the payments wallet's nonce at the
latest block, the base fee and the priority fee) the program proves one exact
EIP-1559 transaction from the payments wallet to the token, with value 0 and the
Pakt's gas limit:
- a payment is
transferFrom(master, payee, amount), within the token's and the payee's ceilings; - a return is
transfer(master, amount)of the payments wallet's own balance. It is always allowed for a bound chain and a listed token, bounded only by that balance.
The maximum fee is at least the inclusion floor Permit2 approvals also use (the
attested base fee grown for the oldest block the attestor accepts, plus the
priority fee), or twice the base fee plus the priority fee if higher, capped at
the Pakt's ceiling. The Pakt's gas limit must be at least 80,000, enough for an
ERC-20 transferFrom through a proxied token. On Arbitrum the gas limit also
pays for the transaction's L1 data in L2 gas units, so set it well above that
floor there (for example 2,000,000). When the ceiling is below the
floor, when the master's allowance or balance (or, for a return, the payments
wallet's balance) is too low, or when a listed payee's window has not opened
yet, Pakt refuses before proving, names the value required and records
nothing, so the same request can be sent again later. An Allow returns the
signed transaction for the agent to submit. Each transfer uses the nonce of the
latest block, so wait until one is mined before proposing the next; a request
while an earlier signed transfer still holds the nonce is refused the same way,
naming the nonce. Report the transaction hash, under the response's
deciding_pakt_root, to record the result; Pakt checks it against the exact
signed transaction.
Known limitation: no fee bump. The floor covers the base fee's largest possible growth over the oldest block the attestor accepts plus one block. It does not cover a long proof or a late broadcast: if the base fee rises past the signed fee before the agent broadcasts, the transaction waits until fees fall, and its nonce stays reserved until it is mined. Pakt has no path to replace a signed transaction with a higher fee; the agent broadcasts the same transaction once fees allow.
Budgets and Payee Limits
A paying services Pakt (one with a service-account) and a payments Pakt can
add two optional rules at their own scope (venue services or payments,
instance 1). Both are checked inside the proof.
payment-budgets: up to 16 budgets. Each sums the tokens of itsgroup(entries{"chain_id", "token"}from the Pakt'spayment-assets, which the owner declares equivalent; every payment token has 6 decimals; in a payments Pakt every token of a group is on one chain) for the payments itsscopeselects in itswindow, and caps the total atcap_atomic(atomic units, a decimal string) and, optionally, the number of payments atmax_count.scope:{"kind":"all"};{"kind":"tag","tag":"refill"}for every listed payee carrying that tag; or{"kind":"payee","chain_id":8453,"address":"0x…"}. A payee inpayment-payeesmay carry up to fourtags(plain text, at most 32 characters, distinct). In a payments Pakt the defaultrefillpayee is taggedrefill.window:{"kind":"trailing","secs":86400}ends at the attested time and lasts 1 hour to 31 days;{"kind":"calendar","period":"day"},"week"or"month"is the current UTC calendar period, starting exactly at UTC midnight, a week on Monday.returns_to(services only): the owner's master per chain, whose plain returns do not count.
payee-recurrence: per listed payee (chain_id,address), at mostmax_amount_atomicandmax_paymentspayments perperiod(a window as above), summed over every payment token on that payee's chain.
{"kind":"payment-budgets","venue":"payments","instance":1,"params":{"budgets":[
{"group":[{"chain_id":8453,"token":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"},
{"chain_id":42161,"token":"0xaf88d065e77c8cc2239327c5edb3a432268e5831"}],
"scope":{"kind":"all"},"window":{"kind":"calendar","period":"day"},
"cap_atomic":"100000000"},
{"group":[{"chain_id":8453,"token":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"}],
"scope":{"kind":"tag","tag":"refill"},"window":{"kind":"trailing","secs":604800},
"cap_atomic":"50000000","max_count":3}
]}}What counts. A limit counts the account's real outflow, not only what Pakt signed:
- Payments: every transfer of the group's tokens out of the master on its
chain, to any destination, including transfers you send from the master
yourself outside Pakt. A payments Pakt names no
returns_to. - Service account: every transfer out of the payments wallet, except a plain
transfer back to the
returns_tomaster on that chain. A payment executed through the x402 Permit2 proxy always counts, even to that master. - Plus what Pakt signed that the chain does not show yet: transfers not yet final, and authorizations not yet used. An entry stops counting as pending once a finalized block shows it used (it then counts from history), or once an authorization has expired and the token reports it unused at a finalized block.
Only the limits a payment falls under are checked, and a payment is allowed when the window's settled total, the pending entries and the payment itself stay at or below each cap and count. The review shows each budget and payee limit, what counts, and the restart bound below.
History. The attestor reads each chain's transfer history from the earliest window start up to a latest block at most 120 seconds older than the attested time. A read with more logs than the attestor caps is marked incomplete, and the payment is refused, never estimated: a gap is not zero spend. An account that sends many small transfers can therefore hit the cap before the budget; a shorter window reads less history.
Restart bound. After an enclave restart, or when the owner replaces the
Pakt, a host could withhold the pending entries. Direct transfers (payments) use
the payments wallet's sequential nonce on each chain, and a payments budget
covers one chain, so a hidden pending transfer is signed again at the same
nonce, only one of the two can execute, and it counts from history once mined:
a restart cannot overspend. A service account's EIP-3009 and Permit2
authorizations have no such nonce: each restart can exceed a service budget by
at most 16 unexpired authorizations in total, across the budget's chains, each
within the per-payment ceilings, and only until they expire, within the service
account's max_validity_secs.
Zero-value transfers move nothing and never count. A payment reads only the history of the limits it falls under, and a return reads none.
| Refusal | Meaning |
|---|---|
BudgetExceeded | The window's settled total, the pending entries and this payment exceed a budget's cap or count. It passes only once older transfers leave the window or the window rolls over |
RecurrenceExceeded | The same for a payee limit |
SpendHistoryIncomplete | The attested history was over the attestor's log cap, or read at a block too far behind the attested time |
SpendHistoryUnavailable | The attested history does not answer what the limits read: a window start, chain, token or account is missing or different |
PolicyStateCapacityExceeded | 16 payments these limits count are signed but not yet final; wait until they are mined and final, or their authorizations expire |
A new Pakt cannot include a limit that can never apply or that the program
would refuse: a group token outside payment-assets (or, for payments, on a
chain without a bound master), a tag no payee on the group's chains carries, a
payee scope the Pakt can never pay, a payee limit for a payee not listed on its
chain, returns_to in a payments Pakt or naming the payments wallet itself,
more than 16 budgets, or a rule over the 2048-byte constraint limit. Budgets
and payee limits on a services Pakt without a service-account are refused.
Account Conditions
| Rule | What Pakt checks against the projected account |
|---|---|
| Concentration cap | No single asset exceeds the chosen share of the account's gross value before the action, nor of the least gross value the account can have once the orders Pakt already signed fill |
| Liquid reserve | Liquid funds represent at least the chosen share of gross value |
| Equity floor | Account equity is at least the chosen dollar amount |
| Leverage cap | Projected total position notional divided by authenticated account equity is within the chosen limit |
| Health floor | Equity is at least the chosen multiple of required maintenance margin |
For Hyperliquid, these rules start from a fresh account snapshot and add the
worst-case risk from the current batch and every prior signed risk increase whose
30-second venue expiry has not elapsed. Risk-increasing orders are assumed to
fill; risk-reducing orders are assumed not to fill. A batch that sells one asset
and buys two therefore must remain valid even if only the buys fill. For
concentration, Pakt also assumes that every live order, including a
reduce-only close and a sell that is not marked reduce-only, closes as much of
another asset's position as its size allows, which shrinks the account's gross
value. This covers orders signed before the action; a reduce-only close signed
after it, which account limits never refuse, can still raise an asset's share.
Each order is valued at its limit price or the mark price when it was signed,
whichever is higher; an isolated order's margin is credited at the lower of the
two, and a buy's taker fee is taken at its limit, the most it can pay. Pakt
deducts the taker fee and the loss from filling at a limit worse than that mark
from equity and from the gross value concentration uses, including for
reduce-only closes. A close's cost stays deducted until the close's venue
expiry passes, even if it has filled, so an expensive close can hold back new
orders until then. These checks use the venue-selected leverage to
conservatively reserve initial margin and liquidity, but that venue setting is
not itself capped by the Pakt. An isolated order's margin is credited at the
leverage selected when it was signed; if the leverage is changed while the
order is still live, the estimate can be stale until that order's venue expiry,
at most five minutes.
Pakt checks account limits only when it authorizes a new action, against its
estimate of the account after that action. It does not watch the account
afterwards or close positions, so the account can be past a limit at any time
after the check. The dashboard lists these rules under Refuses new orders
when.
An order signed by Pakt keeps reserving this headroom through its venue expiry,
even if it is never submitted. Snapshot times are whole Unix seconds. For expiry
E in milliseconds, the first snapshot that drops the reservation is at
floor(E / 1000) + 1 seconds; equality still counts. Refusal diagnostics report
this as first_unreserved_as_of_secs for each reservation. Waiting only helps
when a fresh attested snapshot reaches the relevant second, not merely when
the local clock passes E, and does not guarantee that other rules will pass.
Reservation rows count committed risk batches, not distinct signed orders. One
mixed-margin request can contribute a cross, an isolated, and a reduce-only
reservation batch. A reduce-only batch adds no exposure; it stays live through
its venue expiry so concentration can account for what it may close and its
fee and worst fill stay deducted. If the reservation list is full, a pure
reduce-only close is still approved; Pakt then refuses any order that is not
entirely reduce-only until that close's venue expiry has passed. A reduce-only
close expires at most five minutes after it is signed, so this wait extends
only while further closes keep arriving with the list still full. The
PolicyStateCapacityExceeded refusal reports the second the wait ends, and
when the earliest live reservation is released.
Timing and Data
| Control | What it specifies |
|---|---|
| Trading hours | An inclusive daily UTC window for the authenticated action time |
| Pakt validity | The time after which the Pakt's rules no longer authorize new actions, except a pure reduce-only close |
| Request expiry | The maximum lifetime of a request where the venue enforces an expiry |
| Data freshness | The maximum age of the account snapshot relative to the authenticated action time |
| Accepted data sources | The sources whose signed price, action, and account-state evidence the Pakt accepts |
Trading hours constrain the time checked by Pakt. A request's expiry separately limits late submission where supported. An Ethereum token approval has no generic wall-clock expiry: once signed, it remains usable until its nonce is consumed. Disabling a Pakt does not invalidate an already released signature.
Closing a Hyperliquid Position
A venue-enforced reduce-only order can reduce an existing position without increasing exposure. Pakt first verifies the authenticated position is on the opposite side and that the batch's total reduce-only size does not exceed it. Only then is the close exempt from the trade-size cap, batch notional budget, price band, and account risk thresholds. A batch made entirely of such closes is also approved outside trading hours and after the Pakt's validity time, so neither can trap an open position. Pakts committed to a retained policy program (V6 or older) keep trading hours and validity for closes too. A close adds no exposure to later checks; Pakt keeps only how much it can close and what it can cost.
Other applicable checks still apply, including market and side permissions, action counts, fees, margin mode, and request expiry. A pure venue-enforced reduce-only batch receives a bounded five-minute emergency expiry window instead of the 30-second entry window, so proof and submission retries do not trap an exit. Mixed batches retain the 30-second bound. Asking the agent to “close” a position is not enough; every order in the batch must be reduce-only. When a mixed batch fails only rules its reduce-only legs alone would pass, such as account risk thresholds or the trade-size cap, the refusal says so; send the reduce-only legs as a separate batch with a fresh nonce.
Selected Markets
Pakt's current Hyperliquid policy program supports eligible default and HIP-3
perpetual markets using USDC collateral, in cross or isolated margin. Isolated
margin is treated the same whether Hyperliquid requires it for a market or you
selected it on a market that also supports cross: each isolated position keeps
its own margin, and the health floor checks it on its own. Each order uses the
margin mode selected for its market on Hyperliquid, and the Pakt's margin-mode
rule applies to every order, including reduce-only closes. An existing Pakt
whose margin-mode rule allows isolated margin therefore also authorizes isolated
orders on markets that support cross, as its approved rule allows; a Pakt
restricted to cross refuses them with MarginModeMismatch.
Older Pakts keep the policy-program generation they were approved with, shown
as policy_program_generation by get_execution_context. current,
account_leverage_v6, and mixed_margin_mode_v5 Pakts evaluate isolated
orders and positions normally.
single_margin_mode_v4 Pakts allow one margin mode per order and refuse an
order that mixes cross and isolated markets. cross_only_v3 Pakts refuse every
order while the account holds an isolated position on a market Pakt can trade or
the order uses isolated margin, with policy_program_upgrade_required. An
isolated position on a market Pakt cannot trade, such as a delisted one, does
not trigger that refusal. To leave an older generation,
disable the Pakt, wait the 30-second replacement cooldown, then approve a newly
drafted Pakt. See Activate and disable.
Pakt charges the maintenance margin of every new isolated order at the market's
strictest margin tier. On a market with several tiers, such as BTC, an isolated
entry near the market's maximum leverage can therefore be refused by the health
floor even though Hyperliquid would accept it. For account_leverage_v6 and
mixed_margin_mode_v5 Pakts, the isolated health check credits the new
position with its margin minus the fee but does not subtract the loss of
filling worse than the mark, so without a tight fill-price collar a
high-leverage isolated entry can fill already below its maintenance margin; the
preset ±0.2% collar bounds this loss. The current program deducts that loss.
On a market that supports both modes, Pakt's margin-mode guarantees assume the account's margin setting for that market does not change on Hyperliquid between Pakt's decision and the fill. Only the account owner can change that setting; agents cannot, because Pakt signs only orders.
A cross position on a market Pakt can trade that Hyperliquid has since made isolated-only is unsupported: while it is open, Pakt refuses every order on the account, including reduce-only exits, until that position is closed on Hyperliquid. A cross position on a delisted or otherwise untradable market is valued separately and does not block orders. Spot, delisted, and non-USDC markets are outside this execution path. Your agent uses list_hyperliquid_perp_markets to resolve current market identities before drafting.
An open position on a market Pakt cannot trade, such as a delisted or inactive one, counts at maximum risk, whatever margin mode it uses. Pakt counts its full size as both exposure and maintenance margin and gives it no credit as equity or withdrawable funds. Its isolated margin, if any, still counts toward account value for the liquidity-reserve and concentration limits, as it would on a supported market, but is not checked against the health floor on its own; the account-wide check already charges its full size. Such a position does not stop Pakt from reading the account, so reduce-only exits on supported markets stay allowed. Its full maintenance charge can still make the health floor or leverage limit refuse new orders on supported markets. Pakt cannot close the position itself; close it on Hyperliquid directly, or wait for Hyperliquid to settle a delisted market. Hyperliquid's USDC balance is read once between two position reads at most 10 seconds apart, so a profit spike on such a position during that read can be credited for that one check.
BOB Gateway supports USDT-to-Bitcoin orders on Ethereum, with a finite token approval and a committed Bitcoin payout address. Approval and order creation are authorized separately. The allowed-assets rule checks both actions as native Bitcoin priced by Binance BTCUSDT, so it cannot admit or exclude the USDT input, and a list without exactly that entry refuses every Gateway action. The input is always Ethereum USDT. A Gateway Pakt's expiry is measured on finalized Ethereum block time, which runs behind the clock. Pakt's broader rule vocabulary does not add other transaction types to either integration.
A new Gateway Pakt must include a trade-size cap, price protection, a network-fee cap, and a Bitcoin payout destination; a draft or activation without all four is rejected. The payout destination must be a standard Bitcoin mainnet address (P2PKH, P2SH, P2WPKH, P2WSH, or P2TR), and the review shows that address.
A new Pakt cannot include a rule its policy program never checks. For Gateway that is data freshness (the program dates its data at the moment it is checked), withdrawal destinations, and collateral movement (Gateway makes no withdrawals or collateral moves). For Hyperliquid it is withdrawal destinations and the Bitcoin payout destination, because the Hyperliquid program checks only trades. A Pakt activated before these requirements keeps its approved rules until you disable or replace it, but cannot be re-enabled once disabled.
The Pakt Root
The Pakt root is a cryptographic identifier for the exact compiled rules and the policy program that evaluates them. Changing a limit, market, destination, trusted data source, or program creates a different root.
The readable ALLOWED / NOT ALLOWED summary helps you review those rules. The compiled artifact is authoritative; a chat message or template name cannot change an active Pakt.
Your Pakt signer's approval binds that root to your account and environment. Another account having the same rules does not grant it signing or wallet access.
To change a Pakt, draft a replacement and approve its new root.
For the encoding, typed payload, and canonical identity, see The Pakt Artifact.