Skip to content

Pallet advanced limit orders#2554

Merged
sam0x17 merged 94 commits into
opentensor:devnet-readyfrom
girazoki:girazoki-palle-advanced-orders
Jun 4, 2026
Merged

Pallet advanced limit orders#2554
sam0x17 merged 94 commits into
opentensor:devnet-readyfrom
girazoki:girazoki-palle-advanced-orders

Conversation

@girazoki
Copy link
Copy Markdown
Contributor

@girazoki girazoki commented Apr 1, 2026

Description

Introduces pallet-limit-orders, a new FRAME pallet that allows users to sign limit orders off-chain and have relayers submit them on-chain. Orders are executed against subnet AMM pools, supporting three order types: LimitBuy, TakeProfit, and StopLoss.

Two execution paths:

  • execute_orders — processes orders individually, best-effort. Invalid/expired/price-unmet orders are silently skipped; the call always returns Ok.
  • execute_batched_orders(netuid, orders) — atomic batch for a single subnet. Nets buy and sell sides before touching the AMM pool (reduced price impact). Fails the entire call if any order
    is invalid.

Validation enforced on both paths:

  • Sr25519-only signatures (Ed25519|ECDSA rejected for now, there is a possibility to add in the future)
  • Expiry checks against wall-clock time (pallet_timestamp).
  • Price condition checks against current AMM spot price.
  • Minimum stake enforcement (delegated to pallet_subtensor) per order, but not applicable for inner-pallet operations
  • Hotkey–coldkey association required for order creators, but not for the inner pallet for now (although can be added).
  • SubtokenEnabled must be true for the target subnet.
  • Root netuid (0) explicitly blocked.

Fee model: per-order fee_rate: Perbill + fee_recipient: AccountId fields on the order struct. No global protocol fee. Fees are batched per unique recipient and transferred in a single call per recipient at the end of each batch. Enables flexibility of fee-percentage and fee-account per order.

Replay protection: executed and cancelled orders are written to Orders: StorageMap<H256, OrderStatus> by their blake2_256 hash.


Open questions / TODOs

  • LastColdkeyHotkeyStakeBlock on pallet intermediary account — when execute_batched_orders transfers alpha from sellers into the pallet intermediary account, should LastColdkeyHotkeyStakeBlock be updated for the pallet account? If the block tracking is used for rate-limiting or cooldown enforcement, intermediate transfers through the pallet account could trigger unintended restrictions on subsequent operations in the same block.
    Answer: The pallet should not be rate-limited as it would break it's existence. rate-limiting is already applied to the cold keys that use the pallet so we are protected against that, but the pallet does operations like collecting all TAO/alpha and then distribute in batch mode. so no, we should not rate-limit

  • Hotkey ownership verification for the pallet intermediary — the pallet intermediary account stakes/unstakes via PalletHotkey without going through the normal hotkey–coldkey association checks. Should we add an explicit ownership or registration check, or is the pallet account's privileged position sufficient justification to bypass it?

  • Auto-associate PalletHotkey with the pallet coldkey on first use — if the pallet hotkey is not yet registered/associated with the pallet's derived coldkey, another cold-key could potentially claim the hotkey being used by the pallet.

  • TypeScript / e2e tests — no TypeScript integration tests yet. These should cover: order signing, relayer submission flow, fee collection, and the batched netting path against a live node.

  • Since it's a complex feature, I would like to add isPalletEnabled storage item, initially set to false, so that it can be enabled in testnet but not in mainnet

  • Benchmarks

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Other (please describe):

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have run ./scripts/fix_rust.sh to ensure my code is formatted and linted correctly
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Additional Notes

I moved pallets/swap-interface to primitives/swap-interface. it was not a pallet, so I thought it belonged more there

Comment thread pallets/limit-orders/src/lib.rs Outdated
Comment thread pallets/subtensor/src/staking/order_swap.rs Outdated
Comment thread pallets/limit-orders/src/lib.rs Outdated
)?;

// Forward the fee TAO to the order's fee recipient.
Self::forward_fee(&order.signer, &order.fee_recipient, fee_tao);
Copy link
Copy Markdown
Contributor

@open-junius open-junius May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should handle the failure of forward_fee. otherwise the user could use fee_rate and its balance to avoid pay it.

#2685 (comment)

gztensor
gztensor previously approved these changes May 27, 2026
Copy link
Copy Markdown
Collaborator

@l0r1s l0r1s left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just taking a look at the meta-tx pallet in the polkadot-sdk so we can get the relayer logic for any extrinsic and not coupled to limit orders.

@gorka-i
Copy link
Copy Markdown

gorka-i commented Jun 1, 2026

Looks good, just taking a look at the meta-tx pallet in the polkadot-sdk so we can get the relayer logic for any extrinsic and not coupled to limit orders.

Not in a straight forward manner for a couple of reasons:

  • this pallet allows the relayer to inject orders coming from the same user in whatever order as long as the orders pass the validations. these would not be true with meta-tx, since each tx would have a nonce, and a tx with nonce 2 would invalidate a previous tx with nonce 1. this does not play well with the pallet, as in our case we submit orders to be executed whenever the price condition satisfies, regardless if it has nonce 1 or 2.

  • meta-tx disables the batched_execution. in our case the relayer is supposed to be smart enough to detect that 2 or more orders happen against the same alpha token, group them together, and execute them in batched mode, influencing less the price than if we execute them individually.

In short is meta-tx a good pallet? definitely, but just by itself it does not seem to fulfill the requirements of this pallet.

@l0r1s l0r1s self-requested a review June 1, 2026 16:49
@l0r1s
Copy link
Copy Markdown
Collaborator

l0r1s commented Jun 1, 2026

@gorka-i make sense, thanks for looking at it

@open-junius
Copy link
Copy Markdown
Contributor

The current implementation looks good to me. I'd like to know if you will continue the PR to handle three issues in the TODO list. I have a suggestion about the intermediary hotkey, we could skip it, not calling transfer alpha. Instead, use the burn_alpha and mint_alpha for buyers and sellers.

@girazoki
Copy link
Copy Markdown
Contributor Author

girazoki commented Jun 3, 2026

The current implementation looks good to me. I'd like to know if you will continue the PR to handle three issues in the TODO list. I have a suggestion about the intermediary hotkey, we could skip it, not calling transfer alpha. Instead, use the burn_alpha and mint_alpha for buyers and sellers.

Some of them have already been done. The only thing left to do is we apply rate-limitting to the pallet account but that would break the nature of it (the pallet might do several operations within a single-block, as it collects in the batched mode all alpha/tao and then distributes). so I'd say is fine if we leave it as is.

Updated the list

@sam0x17 sam0x17 merged commit 75a85ba into opentensor:devnet-ready Jun 4, 2026
215 of 217 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-cargo-audit This PR fails cargo audit but needs to be merged anyway

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants