Skip to content

Commit 3bd304c

Browse files
Add examples to builder codes and revshare (#427)
* Add examples to builder codes and revshare * Remove unnecessary code group formatting from builder codes and revshare documentation * refine docs for builder codes and revenue share
1 parent 446b816 commit 3bd304c

File tree

2 files changed

+140
-113
lines changed

2 files changed

+140
-113
lines changed

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

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,48 +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-
```go
19-
message Order {
20-
// The unique ID of this order. Meant to be unique across all orders.
21-
OrderId order_id = 1 [ (gogoproto.nullable) = false ];
22-
23-
...
24-
25-
// builder_code is the metadata for the partner or builder of an order.
26-
BuilderCodeParameters builder_code_params = 12;
27-
}
28-
29-
// BuilderCodeParameters represents the metadata for the
30-
// partner or builder of an order. This allows them to
31-
// specify a fee for providing there service
32-
// which will be paid out in the event of an order fill.
33-
message BuilderCodeParameters {
34-
// The address of the builder to which the fee will be paid.
35-
string builder_address = 1;
36-
37-
// The fee enforced on the order in ppm.
38-
uint32 fee_ppm = 2;
39-
}
40-
```
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)
4144

42-
:::note
43-
`BuilderCodeParameters` is an optional field
44-
:::
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+
```
4580

4681
## Order Validation Checks
4782
- Ensure the `builder address` is valid
48-
- Ensure the `feePPM` is in the range `(0, 10,000]`
83+
- Ensure the `feePpm` is in the range `(0, 10,000]`
4984

5085
::::

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

Lines changed: 74 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -17,101 +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 is an example of what the governance message structure looks like:
21-
```json
22-
message Order {
23-
"messages": [
24-
{
25-
"@type": "/dydxprotocol.revshare.MsgSetOrderRouterRevShare",
26-
"authority": authority,
27-
"order_router_rev_share": {
28-
"address": {{your address}},
29-
"share_ppm": {{your requested ppm}},
30-
}
31-
}
32-
]
33-
}
34-
```
35-
The key components of this message are:
36-
37-
- `address` - The address of the order router that will receive the revenue share. This is also the id you place in your order message
38-
- `sharePpm` - The revenue share percentage in parts per million (ppm).
39-
40-
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.
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.
4121

42-
## Updating Revenue Share (Optional)
43-
44-
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.
45-
46-
To update the revenue share percentage for an existing order router, create a governance message with the same structure:
47-
48-
```json
49-
"messages": [
50-
{
51-
"@type": "/dydxprotocol.revshare.MsgSetOrderRouterRevShare",
52-
"authority": authority,
53-
"order_router_rev_share": {
54-
"address": {{your existing address}},
55-
"share_ppm": {{your new requested ppm}},
56-
}
57-
}
58-
]
59-
```
22+
For an example of the governance proposal JSON structure, see [proposal 311 on Mintscan](https://www.mintscan.io/dydx/proposals/311).
6023

61-
The proposal must go through the standard governance voting process and receive a passing vote before the updated revenue share percentage takes effect.
24+
The key components of the proposal message are:
6225

63-
:::note
64-
65-
- You must use the exact same address that was previously approved
66-
- The update will completely replace the previous configuration once approved
67-
:::
68-
69-
## Deleting an Order Router Rev Share (Optional)
70-
71-
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.
72-
73-
Submit a governance proposal with the following message structure:
74-
```json
75-
"messages": [
76-
{
77-
"@type": "/dydxprotocol.revshare.MsgSetOrderRouterRevShare",
78-
"authority": authority,
79-
"order_router_rev_share": {
80-
"address": {{your existing address}},
81-
"share_ppm": 0,
82-
}
83-
}
84-
]
85-
```
86-
87-
:::note
88-
Key points to note:
26+
- `address` - The address of the order router that will receive the revenue share. This is also the id you place in your order message
27+
- `share_ppm` - The revenue share percentage in parts per million (ppm)
8928

90-
- Setting `sharePpm` to 0 effectively disables the revenue share for that order router
91-
- The address must match the previously approved order router address
92-
- The proposal must still pass through the standard governance voting process
93-
- Once approved, the order router will no longer receive any revenue share
29+
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.
9430

95-
After the proposal passes, any orders that include this order router address will no longer generate revenue share for the router.
96-
:::
31+
## Placing orders with order rev share address and verifying Order Router Address in Fills
9732

98-
## Changes to the Order Message
9933
The `order_router_address` field is set when an order is placed
10034

10135
- `order_router_address` - the ID of the order router and where fees will be sent to
10236

103-
```go
104-
message Order {
105-
// The unique ID of this order. Meant to be unique across all orders.
106-
OrderId order_id = 1 [ (gogoproto.nullable) = false ];
107-
...
108-
// order_router_address is the metadata for the frontend order router.
109-
string order_router_address = 13;
110-
}
37+
```python [Python]
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
48+
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
52+
from v4_proto.dydxprotocol.clob.order_pb2 import Order
53+
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+
69+
current_block = await node.latest_block_height()
70+
71+
new_order = market.order(
72+
order_id=order_id,
73+
order_type=OrderType.MARKET,
74+
side=Order.Side.SIDE_SELL,
75+
size=0.0001,
76+
price=0, # Recommend set to oracle price - 5% or lower for SELL, oracle price + 5% for BUY
77+
time_in_force=Order.TimeInForce.TIME_IN_FORCE_UNSPECIFIED,
78+
reduce_only=False,
79+
good_til_block=current_block + 10,
80+
order_router_address=TEST_ADDRESS_2,
81+
)
82+
83+
transaction = await node.place_order(
84+
wallet=wallet,
85+
order=new_order,
86+
)
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)
111103
```
112104

113105
## Order Validation Checks
114106

115-
- 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
116108

117109
::::

0 commit comments

Comments
 (0)