Pakt Docs

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

RuleWhat you control
Allowed assetsThe markets or assets your agent may use
Allowed protocolsThe venues or protocols through which actions may be routed
Allowed action kindsWhich supported actions are permitted, such as trading, withdrawals, or sweeps
Allowed order sidesWhich buy, sell, or reduce-only order effects are permitted
Margin modeWhich 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

RuleWhat you control
Trade-size capMaximum dollar value of an individual trade
Notional budgetMaximum combined dollar value of actions checked together
Actions per requestMaximum number of actions checked together
Price protectionAn acceptable price or effective exchange-rate band relative to the authenticated reference price
Network-fee capMaximum dollar value of the network fee for an action
Collateral movementMaximum collateral outflow in a single action
Withdrawal destinationsThe addresses to which withdrawals may be sent
Bitcoin payout destinationOne exact destination for a Bitcoin sweep through BOB Gateway
Payments walletThe wallet a payments Pakt sends from and, per chain, the master it spends from, the gas limit and the fee ceiling
Payment tokens and payeesThe 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 limitsHow 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)TokenAddresseip712_nameeip712_version
Base (8453)USDC0x833589fcd6edb6e08f4c7c32d4f71b54bda02913USD Coin2
Arbitrum One (42161)USDC0xaf88d065e77c8cc2239327c5edb3a432268e5831USD Coin2
Arbitrum One (42161)USDT00xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9USD₮01
Ethereum (1)USDC0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48USD Coin2
Ethereum (1)USDT0xdac17f958d2ee523a2206206994597c13d831ec7""""

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.url equals the endpoint (ignoring its query), or the MPP realm is its host;
  • the method is enabled and the token is one of the pinned 6-decimal USD stablecoins above, listed in payment-assets on that chain, under its per-payment ceiling;
  • the merchant's address on that chain is listed in payment-payees within 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 TransferWithAuthorization or Permit2 transfer the rules allow. Its window (the Permit2 deadline) starts at the attestor's signing time and ends at the soonest of the merchant's limit, max_validity_secs, the payee's not_after and the Pakt's own valid_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-Authenticate value as an mpp challenge. Pakt signs transferWithMemo(recipient, amount, memo), with the memo mppx binds to the challenge, and returns an Authorization: Payment header carrying the signed transaction. The merchant broadcasts it.
  • Session. Add tempo_session with the deposit and the address of a session key the agent generates and keeps. Pakt signs open on the TIP-1034 channel reserve 0x4D50500000000000000000000000000000000000, the escrow mppx sessions use, and never sees the key. The channel's payee must be listed in payment-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_operation intent signs transfer(to, amount) to a listed payee that names no service, and returns the raw transaction for the agent to broadcast. Its operation_id is 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_close and, after the grace period (900 seconds today), withdraw return 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 the refill payee, tagged refill, on every bound chain by default, up to the largest token ceiling there.
  • state-source-config with state source 4, the payments attestor's key.
  • Optionally payment-budgets and payee-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 its group (entries {"chain_id", "token"} from the Pakt's payment-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 its scope selects in its window, and caps the total at cap_atomic (atomic units, a decimal string) and, optionally, the number of payments at max_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 in payment-payees may carry up to four tags (plain text, at most 32 characters, distinct). In a payments Pakt the default refill payee is tagged refill.
    • 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 most max_amount_atomic and max_payments payments per period (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_to master 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.

RefusalMeaning
BudgetExceededThe 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
RecurrenceExceededThe same for a payee limit
SpendHistoryIncompleteThe attested history was over the attestor's log cap, or read at a block too far behind the attested time
SpendHistoryUnavailableThe attested history does not answer what the limits read: a window start, chain, token or account is missing or different
PolicyStateCapacityExceeded16 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

RuleWhat Pakt checks against the projected account
Concentration capNo 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 reserveLiquid funds represent at least the chosen share of gross value
Equity floorAccount equity is at least the chosen dollar amount
Leverage capProjected total position notional divided by authenticated account equity is within the chosen limit
Health floorEquity 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

ControlWhat it specifies
Trading hoursAn inclusive daily UTC window for the authenticated action time
Pakt validityThe time after which the Pakt's rules no longer authorize new actions, except a pure reduce-only close
Request expiryThe maximum lifetime of a request where the venue enforces an expiry
Data freshnessThe maximum age of the account snapshot relative to the authenticated action time
Accepted data sourcesThe 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.

On this page