Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
73da72a
move swap interface to primitives since it's not a pllet
gorka-i Mar 23, 2026
e80c0b3
pallet advancing
gorka-i Mar 23, 2026
4f6b85c
pallet execute_orders_batch and also pallet account
girazoki Mar 24, 2026
e441dc7
start adding tests
girazoki Mar 24, 2026
3010345
fmt plus tests
girazoki Mar 24, 2026
c1e26a1
fmt function
girazoki Mar 24, 2026
ffb1637
fixes here and there
girazoki Mar 24, 2026
bf262b4
readme, refactors and security
girazoki Mar 24, 2026
3f853a9
add an additional test checking we get fees from both sides
girazoki Mar 25, 2026
1407762
Add all order types
girazoki Mar 30, 2026
1c2f173
rename order.side to order_type
girazoki Mar 30, 2026
bf29170
remove order-limits
girazoki Mar 30, 2026
88bfa69
order swap remove
girazoki Mar 30, 2026
7c228a7
remove signature
girazoki Mar 30, 2026
e7d3584
fee shoudl be part of the order, as well as fee account
girazoki Mar 31, 2026
1594eec
add tests regarding fee
girazoki Mar 31, 2026
97eac2d
apply limits, including rates, min stake, etc
girazoki Mar 31, 2026
0a6adbd
keep adding more validations
girazoki Mar 31, 2026
738e7bd
check for order validity
girazoki Mar 31, 2026
c7243cd
first integration tests running
girazoki Mar 31, 2026
2dd7c16
apply filters
girazoki Mar 31, 2026
375f6d8
change errors and add new tests
girazoki Apr 1, 2026
59136d6
fix pallet-iner tests
girazoki Apr 1, 2026
2e70f39
use assert_noop in pallet tests
girazoki Apr 1, 2026
5bb9945
add more tests
girazoki Apr 1, 2026
f3375ed
readme change
girazoki Apr 1, 2026
95397db
better doc
girazoki Apr 1, 2026
ee1520c
be more accurate with numbers plus stoploss test
girazoki Apr 1, 2026
fcedbc0
fee related tests
girazoki Apr 1, 2026
a567b6a
kill switch & fmt
girazoki Apr 1, 2026
358a520
first 2 benchmarks added
girazoki Apr 1, 2026
8d56e16
first placeholder for benches of order exec
girazoki Apr 6, 2026
ed5e698
refactor benches and things running
girazoki Apr 6, 2026
8b58a4c
first ts-tests working
girazoki Apr 6, 2026
152f3df
add more tests
girazoki Apr 6, 2026
3829501
more tests
girazoki Apr 6, 2026
7283b51
weights used
girazoki Apr 7, 2026
968b2ee
adapt to event thrown in execute_orders
girazoki Apr 7, 2026
83d15d4
make orders versioned
girazoki Apr 7, 2026
db0e934
adapt ts-tests
girazoki Apr 7, 2026
b382e0a
all tests working
girazoki Apr 7, 2026
9bff196
be more precise in batched
girazoki Apr 7, 2026
bce34c9
move helpers to dev-helpers
girazoki Apr 7, 2026
966bebd
I few more tests and edge cases
girazoki Apr 7, 2026
58d7865
Relayer protection
girazoki Apr 8, 2026
28a80eb
slippage, transactional changes, and a few more dynamic tests
girazoki Apr 8, 2026
e85911c
new tests for partial fills
girazoki Apr 8, 2026
f4360d1
fix benchmarks
girazoki Apr 8, 2026
fe9c039
fix failing test
girazoki Apr 13, 2026
5ec5087
dev helpers cleanup
girazoki Apr 13, 2026
70a02fd
fee event refactor
girazoki Apr 13, 2026
a5cff7f
changes to make things a bit more efficeint
girazoki Apr 13, 2026
05ba26e
more refactor, specialyl in testing
girazoki Apr 13, 2026
42d22b2
commit Cargo.lock
girazoki Apr 13, 2026
d767749
cargo clippy
girazoki Apr 13, 2026
a26c508
cargo fmt
girazoki Apr 13, 2026
8e284e2
commit Cargo.lock
girazoki Apr 13, 2026
6b88048
Merge remote-tracking branch 'origin/devnet-ready' into girazoki-pall…
girazoki Apr 14, 2026
e294f88
cargo fmt
girazoki Apr 13, 2026
383ab80
add chain-id to avoid replay protection across networks
girazoki Apr 14, 2026
b4985ab
remove non-used func
girazoki Apr 14, 2026
9bdfbe0
Merge remote-tracking branch 'origin/devnet-ready' into girazoki-pall…
girazoki May 4, 2026
c377bfd
fix tests and compilation
girazoki May 5, 2026
56acb7c
Merge remote-tracking branch 'origin/devnet-ready' into girazoki-pall…
girazoki May 6, 2026
64ec16a
zepter and fmt
girazoki May 8, 2026
89cacb0
Let's register coldkey-hotkey on genesis and on_runtime_upgrade
girazoki May 8, 2026
9420e91
fix ecotest
girazoki May 8, 2026
28bd3c1
fmt
girazoki May 8, 2026
a7a7fba
clippy
girazoki May 8, 2026
9eddf66
do not require ownership of coldkey and hotkey when buying or selling…
girazoki May 12, 2026
d4d136e
limit price should come as amm price
girazoki May 12, 2026
0fcc91d
make pallet limit orders be disabled on-rt-upgrade
girazoki May 13, 2026
5c4b5a7
change also validation in swap
girazoki May 13, 2026
8cfc635
add sim_swap to avoid slippage-caused errors
girazoki May 18, 2026
79d4851
Merge remote-tracking branch 'origin/devnet-ready' into girazoki-pall…
girazoki May 20, 2026
2784086
allow a set of relayers to be whitelisted
girazoki May 20, 2026
3123f9d
mevshield tests
girazoki May 20, 2026
91886d7
fixes for checking dev node mevshield
girazoki May 20, 2026
48c8a28
mevshield dev node and tests limit orders
girazoki May 20, 2026
9f625c1
precompile for limit order pallet
open-junius May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ useless_conversion = "allow" # until polkadot is patched
pallet-alpha-assets = { path = "pallets/alpha-assets", default-features = false }
node-subtensor-runtime = { path = "runtime", default-features = false }
pallet-admin-utils = { path = "pallets/admin-utils", default-features = false }
pallet-limit-orders = { path = "pallets/limit-orders", default-features = false }
pallet-commitments = { path = "pallets/commitments", default-features = false }
pallet-registry = { path = "pallets/registry", default-features = false }
pallet-crowdloan = { path = "pallets/crowdloan", default-features = false }
Expand All @@ -72,7 +73,7 @@ subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc
subtensor-custom-rpc-runtime-api = { default-features = false, path = "pallets/subtensor/runtime-api" }
subtensor-precompiles = { default-features = false, path = "precompiles" }
subtensor-runtime-common = { default-features = false, path = "common" }
subtensor-swap-interface = { default-features = false, path = "pallets/swap-interface" }
subtensor-swap-interface = { default-features = false, path = "primitives/swap-interface" }
subtensor-transaction-fee = { default-features = false, path = "pallets/transaction-fee" }
subtensor-chain-extensions = { default-features = false, path = "chain-extensions" }
stp-shield = { git = "https://github.com/opentensor/polkadot-sdk.git", rev = "7cc54bf2d50ae3921d718736dfeb0de9468539c7", default-features = false }
Expand Down
3 changes: 2 additions & 1 deletion chain-extensions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,6 @@ runtime-benchmarks = [
"pallet-subtensor-proxy/runtime-benchmarks",
"pallet-subtensor-utility/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"subtensor-runtime-common/runtime-benchmarks"
"subtensor-runtime-common/runtime-benchmarks",
"subtensor-swap-interface/runtime-benchmarks"
]
263 changes: 263 additions & 0 deletions contract-tests/src/contracts/limitOrders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
export const ILIMITORDERS_ADDRESS =
"0x000000000000000000000000000000000000080e";

export const ILimitOrdersABI = [
{
inputs: [
{
components: [
{ internalType: "address", name: "signer", type: "address" },
{ internalType: "address", name: "hotkey", type: "address" },
{ internalType: "uint16", name: "netuid", type: "uint16" },
{ internalType: "uint8", name: "order_type", type: "uint8" },
{ internalType: "uint64", name: "amount", type: "uint64" },
{ internalType: "uint64", name: "limit_price", type: "uint64" },
{ internalType: "uint64", name: "expiry", type: "uint64" },
{ internalType: "uint32", name: "fee_rate", type: "uint32" },
{ internalType: "address", name: "fee_recipient", type: "address" },
{ internalType: "address[]", name: "relayer", type: "address[]" },
{ internalType: "bool", name: "has_max_slippage", type: "bool" },
{ internalType: "uint32", name: "max_slippage", type: "uint32" },
{ internalType: "uint64", name: "chain_id", type: "uint64" },
{
internalType: "bool",
name: "partial_fills_enabled",
type: "bool",
},
],
internalType: "struct OrderInput",
name: "order",
type: "tuple",
},
],
name: "cancelOrder",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
components: [
{ internalType: "address", name: "signer", type: "address" },
{ internalType: "address", name: "hotkey", type: "address" },
{ internalType: "uint16", name: "netuid", type: "uint16" },
{ internalType: "uint8", name: "order_type", type: "uint8" },
{ internalType: "uint64", name: "amount", type: "uint64" },
{ internalType: "uint64", name: "limit_price", type: "uint64" },
{ internalType: "uint64", name: "expiry", type: "uint64" },
{ internalType: "uint32", name: "fee_rate", type: "uint32" },
{ internalType: "address", name: "fee_recipient", type: "address" },
{ internalType: "address[]", name: "relayer", type: "address[]" },
{ internalType: "bool", name: "has_max_slippage", type: "bool" },
{ internalType: "uint32", name: "max_slippage", type: "uint32" },
{ internalType: "uint64", name: "chain_id", type: "uint64" },
{
internalType: "bool",
name: "partial_fills_enabled",
type: "bool",
},
],
internalType: "struct OrderInput",
name: "order",
type: "tuple",
},
],
name: "deriveOrderId",
outputs: [{ internalType: "bytes32", name: "", type: "bytes32" }],
stateMutability: "view",
type: "function",
},
{
inputs: [
{ internalType: "uint16", name: "netuid", type: "uint16" },
{
components: [
{
components: [
{ internalType: "address", name: "signer", type: "address" },
{ internalType: "address", name: "hotkey", type: "address" },
{ internalType: "uint16", name: "netuid", type: "uint16" },
{ internalType: "uint8", name: "order_type", type: "uint8" },
{ internalType: "uint64", name: "amount", type: "uint64" },
{
internalType: "uint64",
name: "limit_price",
type: "uint64",
},
{ internalType: "uint64", name: "expiry", type: "uint64" },
{ internalType: "uint32", name: "fee_rate", type: "uint32" },
{
internalType: "address",
name: "fee_recipient",
type: "address",
},
{
internalType: "address[]",
name: "relayer",
type: "address[]",
},
{
internalType: "bool",
name: "has_max_slippage",
type: "bool",
},
{
internalType: "uint32",
name: "max_slippage",
type: "uint32",
},
{ internalType: "uint64", name: "chain_id", type: "uint64" },
{
internalType: "bool",
name: "partial_fills_enabled",
type: "bool",
},
],
internalType: "struct OrderInput",
name: "order",
type: "tuple",
},
{ internalType: "bytes", name: "signature", type: "bytes" },
{
internalType: "bool",
name: "has_partial_fill",
type: "bool",
},
{ internalType: "uint64", name: "partial_fill", type: "uint64" },
],
internalType: "struct SignedOrderInput[]",
name: "orders",
type: "tuple[]",
},
],
name: "executeBatchedOrders",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
components: [
{
components: [
{ internalType: "address", name: "signer", type: "address" },
{ internalType: "address", name: "hotkey", type: "address" },
{ internalType: "uint16", name: "netuid", type: "uint16" },
{ internalType: "uint8", name: "order_type", type: "uint8" },
{ internalType: "uint64", name: "amount", type: "uint64" },
{
internalType: "uint64",
name: "limit_price",
type: "uint64",
},
{ internalType: "uint64", name: "expiry", type: "uint64" },
{ internalType: "uint32", name: "fee_rate", type: "uint32" },
{
internalType: "address",
name: "fee_recipient",
type: "address",
},
{
internalType: "address[]",
name: "relayer",
type: "address[]",
},
{
internalType: "bool",
name: "has_max_slippage",
type: "bool",
},
{
internalType: "uint32",
name: "max_slippage",
type: "uint32",
},
{ internalType: "uint64", name: "chain_id", type: "uint64" },
{
internalType: "bool",
name: "partial_fills_enabled",
type: "bool",
},
],
internalType: "struct OrderInput",
name: "order",
type: "tuple",
},
{ internalType: "bytes", name: "signature", type: "bytes" },
{
internalType: "bool",
name: "has_partial_fill",
type: "bool",
},
{ internalType: "uint64", name: "partial_fill", type: "uint64" },
],
internalType: "struct SignedOrderInput[]",
name: "orders",
type: "tuple[]",
},
],
name: "executeOrders",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [],
name: "getLimitOrdersEnabled",
outputs: [{ internalType: "bool", name: "", type: "bool" }],
stateMutability: "view",
type: "function",
},
{
inputs: [{ internalType: "bytes32", name: "orderId", type: "bytes32" }],
name: "getOrderStatus",
outputs: [{ internalType: "uint8", name: "", type: "uint8" }],
stateMutability: "view",
type: "function",
},
] as const;

export type OrderInput = {
signer: string;
hotkey: string;
netuid: number;
order_type: number;
amount: bigint;
limit_price: bigint;
expiry: bigint;
fee_rate: number;
fee_recipient: string;
relayer: string[];
has_max_slippage: boolean;
max_slippage: number;
chain_id: bigint;
partial_fills_enabled: boolean;
};

export const FAR_FUTURE = BigInt("18446744073709551615");

export function buildOrderInput(
signer: string,
hotkey: string,
overrides: Partial<OrderInput> = {},
): OrderInput {
return {
signer,
hotkey,
netuid: 1,
order_type: 0,
amount: BigInt(1_000),
limit_price: BigInt(1_000_000_000),
expiry: FAR_FUTURE,
fee_rate: 0,
fee_recipient: signer,
relayer: [],
has_max_slippage: false,
max_slippage: 0,
chain_id: BigInt(42),
partial_fills_enabled: false,
...overrides,
};
}
52 changes: 52 additions & 0 deletions contract-tests/test/limitOrders.precompile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as assert from "assert";

import { ethers } from "ethers";
import { TypedApi } from "polkadot-api";
import { devnet } from "@polkadot-api/descriptors";
import {
buildOrderInput,
ILIMITORDERS_ADDRESS,
ILimitOrdersABI,
} from "../src/contracts/limitOrders";
import { generateRandomEthersWallet } from "../src/utils";
import { getDevnetApi } from "../src/substrate";
import { forceSetBalanceToEthAddress } from "../src/subtensor";

describe("Limit orders precompile E2E smoke", () => {
let api: TypedApi<typeof devnet>;
let wallet1: ethers.Wallet;
let wallet2: ethers.Wallet;
let limitOrdersContract: ethers.Contract;

beforeEach(async () => {
api = await getDevnetApi();

wallet1 = generateRandomEthersWallet();
wallet2 = generateRandomEthersWallet();
limitOrdersContract = new ethers.Contract(
ILIMITORDERS_ADDRESS,
ILimitOrdersABI,
wallet1,
);

await forceSetBalanceToEthAddress(api, wallet1.address);
await forceSetBalanceToEthAddress(api, wallet2.address);
});

it("reads pallet status through the precompile", async () => {
const enabled = await limitOrdersContract.getLimitOrdersEnabled();
assert.strictEqual(enabled, true);
});

it("derives order ids and cancels orders through the precompile", async () => {
const order = buildOrderInput(wallet1.address, wallet2.address);

const orderId = await limitOrdersContract.deriveOrderId(order);
assert.strictEqual(await limitOrdersContract.getOrderStatus(orderId), 0);

const tx = await limitOrdersContract.cancelOrder(order);
await tx.wait();

assert.strictEqual(await limitOrdersContract.getOrderStatus(orderId), 3);
});
});
Loading
Loading