Skip to content

Commit 55fb2f1

Browse files
committed
refine docs for builder codes and revenue share
1 parent 2a7d632 commit 55fb2f1

File tree

2 files changed

+118
-143
lines changed

2 files changed

+118
-143
lines changed

docs/pages/interaction/integration/integration-builder-codes.mdx

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,68 +3,83 @@
33
Builder codes enables external parties to submit orders to dYdX and collect fees (per-order) for building and routing an order to the exchange.
44
The address and fee, in parts per million, needs to be configured via the `BuilderCodeParameters` in the order message itself. The fee will be paid out when the given order is filled.
55

6+
**Important:** Builder code fees are added **on top of each fill**, as opposed to revenue share where the fee revenue is split. No governance proposal is required to use builder codes.
7+
68
Builder fees and addresses can be queried via the indexer using the `/orders` and `/fills` endpoints as usual. `/orders` contains details on the fee rate and builder address. `/fills` also contains the builder address as well as details on the amount charged per-fill.
79

810

9-
## Changes To The Order Message
11+
# Placing orders and verifying Order Router Address in Fills
1012

1113
::::steps
1214

1315
## BuilderCodeParameters
1416
`BuilderCodeParameters` is an addition to the order message which will specify:
15-
- `partner address` - where fees will be routed
16-
- `fee (in ppm)` that will be charged on order matching
17-
18-
```typescript [TypeScript]
19-
// BuilderCodeParameters interface
20-
export interface IBuilderCodeParameters {
21-
builderAddress: string; // Address to receive the fee
22-
feePpm: number; // Fee in parts per million
23-
}
24-
25-
// Example: Place an order with builder code parameters
26-
const order: IPlaceOrder = {
27-
clientId: 2,
28-
clobPairId: 1,
29-
side: Order_Side.SIDE_BUY,
30-
quantums: Long.fromNumber(1000000),
31-
subticks: Long.fromNumber(50000),
32-
timeInForce: Order_TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
33-
reduceOnly: false,
34-
orderFlags: OrderFlags.SHORT_TERM,
35-
goodTilBlock: currentBlockHeight + 20,
36-
builderCodeParameters: {
37-
builderAddress: 'dydx1example_builder_address',
38-
feePpm: 1000, // 0.1% fee (1000 parts per million)
39-
}
40-
};
41-
42-
await client.validatorClient.post.placeOrder(
43-
subaccount,
44-
order.clientId,
45-
order.clobPairId,
46-
order.side,
47-
order.quantums,
48-
order.subticks,
49-
order.timeInForce,
50-
order.orderFlags,
51-
order.reduceOnly,
52-
order.goodTilBlock,
53-
undefined,
54-
0,
55-
undefined,
56-
undefined,
57-
order.builderCodeParameters,
58-
undefined
59-
);
60-
```
17+
- `builderAddress` - where fees will be routed
18+
- `feePpm (in ppm)` that will be charged on order matching
19+
20+
```python [Python]
21+
import asyncio
22+
import random
23+
24+
from dydx_v4_client import MAX_CLIENT_ID, OrderFlags
25+
from v4_proto.dydxprotocol.clob.order_pb2 import Order
26+
27+
from dydx_v4_client.indexer.rest.constants import OrderType
28+
from dydx_v4_client.indexer.rest.indexer_client import IndexerClient
29+
from dydx_v4_client.network import TESTNET
30+
from dydx_v4_client.node.client import NodeClient
31+
from dydx_v4_client.node.market import Market
32+
from dydx_v4_client.wallet import Wallet
33+
from tests.conftest import DYDX_TEST_MNEMONIC, TEST_ADDRESS
34+
35+
MARKET_ID = "ETH-USD"
36+
37+
node = await NodeClient.connect(TESTNET.node)
38+
indexer = IndexerClient(TESTNET.rest_indexer)
39+
40+
market = Market(
41+
(await indexer.markets.get_perpetual_markets(MARKET_ID))["markets"][MARKET_ID]
42+
)
43+
wallet = await Wallet.from_mnemonic(node, DYDX_TEST_MNEMONIC, TEST_ADDRESS)
6144

62-
:::note
63-
`BuilderCodeParameters` is an optional field
64-
:::
45+
order_id = market.order_id(
46+
TEST_ADDRESS, 0, random.randint(0, MAX_CLIENT_ID), OrderFlags.SHORT_TERM
47+
)
48+
49+
current_block = await node.latest_block_height()
50+
51+
new_order = market.order(
52+
order_id=order_id,
53+
order_type=OrderType.MARKET,
54+
side=Order.Side.SIDE_SELL,
55+
size=size,
56+
price=0, # Recommend set to oracle price - 5% or lower for SELL, oracle price + 5% for BUY
57+
time_in_force=Order.TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
58+
reduce_only=False,
59+
good_til_block=current_block + 10,
60+
builder_address=TEST_ADDRESS,
61+
fee_ppm=500,
62+
)
63+
64+
transaction = await node.place_order(
65+
wallet=wallet,
66+
order=new_order,
67+
)
68+
69+
print(transaction)
70+
wallet.sequence += 1
71+
72+
await asyncio.sleep(5)
73+
74+
fills = await indexer.account.get_subaccount_fills(
75+
address=TEST_ADDRESS, subaccount_number=0, ticker=MARKET_ID, limit=1
76+
)
77+
print(f"Fills: {fills}")
78+
assert fills["fills"][0]["builderAddress"] == TEST_ADDRESS
79+
```
6580

6681
## Order Validation Checks
6782
- Ensure the `builder address` is valid
68-
- Ensure the `feePPM` is in the range `(0, 10,000]`
83+
- Ensure the `feePpm` is in the range `(0, 10,000]`
6984

7085
::::

docs/pages/interaction/integration/integration-revshare.mdx

Lines changed: 52 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -17,133 +17,93 @@ To participate in the Order Router Rev Share program, users need to propose and
1717

1818
## Voting in via Governance
1919

20-
To participate in the Order Router Rev Share program, you need to create and submit a governance proposal. Below are examples of the governance message structure:
20+
To participate in the Order Router Rev Share program, you need to create and submit a governance proposal. Since governance proposals require adding gas, deposit, and other parameters, it's recommended to create the proposal using a JSON file and submit it via CLI command.
2121

22-
```python [Python]
23-
# Example: Set order router revenue share via governance
24-
from dydx_v4_client.node.client import NodeClient
22+
For an example of the governance proposal JSON structure, see [proposal 311 on Mintscan](https://www.mintscan.io/dydx/proposals/311).
2523

26-
# This method creates the governance message
27-
# Note: This requires governance authority to execute
28-
async def set_order_router_revenue_share_example():
29-
node = await NodeClient.connect(network.node)
30-
31-
# Set order router revenue share
32-
# authority: Governance authority address
33-
# address: Order router address that will receive revenue share
34-
# share_ppm: Revenue share in parts per million
35-
response = await node.set_order_router_revenue_share(
36-
authority="dydx1...", # Governance authority
37-
address="dydx1your_router_address", # Your order router address
38-
share_ppm=5000 # 0.5% revenue share (5000 parts per million)
39-
)
40-
41-
print(response)
42-
```
43-
44-
The key components of this message are:
24+
The key components of the proposal message are:
4525

4626
- `address` - The address of the order router that will receive the revenue share. This is also the id you place in your order message
47-
- `sharePpm` - The revenue share percentage in parts per million (ppm).
27+
- `share_ppm` - The revenue share percentage in parts per million (ppm)
4828

4929
After submitting the proposal, it must go through the standard governance voting process and receive a passing vote before the order router address and revenue share percentage are activated in the system.
5030

51-
## Updating Revenue Share (Optional)
52-
53-
The process for updating an existing order router's revenue share is the same as setting up a new one. You will need to submit a governance proposal with the updated parameters.
54-
55-
To update the revenue share percentage for an existing order router, create a governance message with the same structure:
56-
57-
```python [Python]
58-
# Update existing order router revenue share
59-
async def update_order_router_revenue_share_example():
60-
node = await NodeClient.connect(network.node)
61-
62-
# Update with new revenue share percentage
63-
# Note: address must match the previously approved order router address
64-
response = await node.set_order_router_revenue_share(
65-
authority="dydx1...", # Governance authority
66-
address="dydx1your_existing_router_address", # Existing approved address
67-
share_ppm=7500 # New revenue share: 0.75% (7500 parts per million)
68-
)
69-
70-
print(response)
71-
```
72-
73-
The proposal must go through the standard governance voting process and receive a passing vote before the updated revenue share percentage takes effect.
74-
75-
:::note
76-
77-
- You must use the exact same address that was previously approved
78-
- The update will completely replace the previous configuration once approved
79-
:::
80-
81-
## Deleting an Order Router Rev Share (Optional)
82-
83-
To delete an order router's revenue share configuration, you simply need to set the revenue share percentage to 0. This process follows the same governance workflow as setting up or updating a revenue share.
84-
85-
Submit a governance proposal with the following message structure:
86-
87-
```python [Python]
88-
# Delete order router revenue share by setting share_ppm to 0
89-
async def delete_order_router_revenue_share_example():
90-
node = await NodeClient.connect(network.node)
91-
92-
# Set share_ppm to 0 to disable revenue share
93-
# Note: address must match the previously approved order router address
94-
response = await node.set_order_router_revenue_share(
95-
authority="dydx1...", # Governance authority
96-
address="dydx1your_existing_router_address", # Existing approved address
97-
share_ppm=0 # Setting to 0 disables revenue share
98-
)
99-
100-
print(response)
101-
```
102-
103-
:::note
104-
Key points to note:
105-
106-
- Setting `sharePpm` to 0 effectively disables the revenue share for that order router
107-
- The address must match the previously approved order router address
108-
- The proposal must still pass through the standard governance voting process
109-
- Once approved, the order router will no longer receive any revenue share
31+
## Placing orders with order rev share address and verifying Order Router Address in Fills
11032

111-
After the proposal passes, any orders that include this order router address will no longer generate revenue share for the router.
112-
:::
113-
114-
## Changes to the Order Message
11533
The `order_router_address` field is set when an order is placed
11634

11735
- `order_router_address` - the ID of the order router and where fees will be sent to
11836

11937
```python [Python]
120-
# Place an order with order router address for revenue share
38+
import asyncio
39+
import random
40+
import time
41+
42+
from dydx_v4_client import MAX_CLIENT_ID, OrderFlags
43+
from dydx_v4_client.indexer.rest.constants import OrderType
44+
from dydx_v4_client.indexer.rest.indexer_client import IndexerClient
45+
from dydx_v4_client.key_pair import KeyPair
46+
from dydx_v4_client.network import TESTNET
47+
from dydx_v4_client.node.client import NodeClient
12148
from dydx_v4_client.node.market import Market
49+
from dydx_v4_client.node.message import order_id
50+
from dydx_v4_client.wallet import Wallet
51+
from tests.conftest import TEST_ADDRESS_2, TEST_ADDRESS, DYDX_TEST_MNEMONIC
12252
from v4_proto.dydxprotocol.clob.order_pb2 import Order
12353

124-
market = Market(market_data)
54+
55+
node = await NodeClient.connect(TESTNET.node)
56+
indexer = IndexerClient(TESTNET.rest_indexer)
57+
MARKET_ID = "ETH-USD"
58+
market = Market(
59+
(await indexer.markets.get_perpetual_markets(MARKET_ID))["markets"][
60+
MARKET_ID
61+
]
62+
)
63+
wallet = await Wallet.from_mnemonic(node, DYDX_TEST_MNEMONIC, TEST_ADDRESS)
64+
65+
order_id = market.order_id(
66+
TEST_ADDRESS, 0, random.randint(0, MAX_CLIENT_ID), OrderFlags.SHORT_TERM
67+
)
68+
12569
current_block = await node.latest_block_height()
12670

12771
new_order = market.order(
12872
order_id=order_id,
12973
order_type=OrderType.MARKET,
13074
side=Order.Side.SIDE_SELL,
13175
size=0.0001,
132-
price=0, # Market order
76+
price=0, # Recommend set to oracle price - 5% or lower for SELL, oracle price + 5% for BUY
13377
time_in_force=Order.TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
13478
reduce_only=False,
13579
good_til_block=current_block + 10,
136-
order_router_address='dydx1your_router_address', # Order router address (must be voted in via governance)
80+
order_router_address=TEST_ADDRESS_2,
13781
)
13882

13983
transaction = await node.place_order(
14084
wallet=wallet,
14185
order=new_order,
14286
)
87+
88+
print(transaction)
89+
90+
await asyncio.sleep(5)
91+
92+
fills = await indexer.account.get_subaccount_fills(
93+
address=TEST_ADDRESS, subaccount_number=0, limit=1
94+
)
95+
96+
print(f"Fills: {fills}")
97+
98+
response = await node.get_market_mapper_revenue_share_param()
99+
print(response)
100+
101+
response = await node.get_order_router_revenue_share(TEST_ADDRESS_2)
102+
print(response)
143103
```
144104

145105
## Order Validation Checks
146106

147-
- Ensure the `order_router_address` field is valid and already voted in via governance
107+
- Ensure the `order_router_address`(TEST_ADDRESS_2) field is valid and already voted in via governance
148108

149109
::::

0 commit comments

Comments
 (0)