Skip to content

Commit 50401c1

Browse files
committed
fix: convert builder_fee bps to chain units before sending to contract
- Add bps_to_chain_units() utility function to convert basis points to chain units (multiply by 100, aligning with FEE_PRECISION = 10000 in contract) - Update place_order() and place_twap_order() to convert builder_fee/builder_fees - Update approve_max_builder_fee() to accept int|float and convert to chain units - Sync both async and sync versions of these methods - Update ApproveBuilderFeeArgs type definition to accept int|float This matches the TypeScript SDK implementation and fixes precision issues.
1 parent 061b0da commit 50401c1

3 files changed

Lines changed: 40 additions & 9 deletions

File tree

src/decibel/_utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"round_to_valid_order_size",
3636
"amount_to_chain_units",
3737
"chain_units_to_amount",
38+
"bps_to_chain_units",
3839
"extract_vault_address_from_create_tx",
3940
"generate_random_replay_protection_nonce",
4041
]
@@ -385,6 +386,21 @@ def amount_to_chain_units(amount: float, decimals: int = 6) -> int:
385386
return round(amount * (10**decimals))
386387

387388

389+
def bps_to_chain_units(bps: int | float) -> int:
390+
"""Convert basis points (bps) to chain units for builder fees.
391+
392+
The contract uses FEE_PRECISION = 10000 to represent 1%.
393+
Since a basis point is 1/100th of a percent, multiply by 100.
394+
395+
Args:
396+
bps: Basis point value (e.g. 10 for 0.1%)
397+
398+
Returns:
399+
Chain units as integer (e.g. 10 bps -> 1000)
400+
"""
401+
return round(bps * 100)
402+
403+
388404
def chain_units_to_amount(chain_units: int, decimals: int = 6) -> float:
389405
"""Convert chain units to a decimal amount (e.g., 5670000 -> 5.67)."""
390406
return chain_units / (10**decimals)

src/decibel/write/__init__.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from decibel._subaccount_types import RenameSubaccount, RenameSubaccountArgs
1717
from decibel._transaction_builder import InputEntryFunctionData
1818
from decibel._utils import (
19+
bps_to_chain_units,
1920
get_market_addr,
2021
get_primary_subaccount_addr,
2122
post_request,
@@ -270,6 +271,9 @@ async def place_order(
270271
if sl_limit_price is not None and tick_size
271272
else sl_limit_price
272273
)
274+
final_builder_fee = (
275+
bps_to_chain_units(builder_fee) if builder_fee is not None else None
276+
)
273277

274278
pkg = self._config.deployment.package
275279

@@ -293,7 +297,7 @@ async def _send(addr: str) -> dict[str, Any]:
293297
final_sl_trigger,
294298
final_sl_limit,
295299
builder_addr,
296-
builder_fee,
300+
final_builder_fee,
297301
],
298302
),
299303
account_override,
@@ -359,6 +363,9 @@ async def place_twap_order(
359363
) -> PlaceOrderResult:
360364
market_addr = get_market_addr(market_name, self._config.deployment.perp_engine_global)
361365
pkg = self._config.deployment.package
366+
final_builder_fees = (
367+
bps_to_chain_units(builder_fees) if builder_fees is not None else None
368+
)
362369

363370
async def _send(addr: str) -> dict[str, Any]:
364371
return await self._send_tx(
@@ -375,7 +382,7 @@ async def _send(addr: str) -> dict[str, Any]:
375382
twap_frequency_seconds,
376383
twap_duration_seconds,
377384
builder_address,
378-
builder_fees,
385+
final_builder_fees,
379386
],
380387
),
381388
account_override,
@@ -1049,17 +1056,18 @@ async def approve_max_builder_fee(
10491056
self,
10501057
*,
10511058
builder_addr: str,
1052-
max_fee: int,
1059+
max_fee: int | float,
10531060
subaccount_addr: str | None = None,
10541061
) -> dict[str, Any]:
10551062
pkg = self._config.deployment.package
1063+
final_max_fee = bps_to_chain_units(max_fee)
10561064

10571065
async def _send(addr: str) -> dict[str, Any]:
10581066
return await self._send_tx(
10591067
InputEntryFunctionData(
10601068
function=f"{pkg}::dex_accounts_entry::approve_max_builder_fee_for_subaccount",
10611069
type_arguments=[],
1062-
function_arguments=[addr, builder_addr, max_fee],
1070+
function_arguments=[addr, builder_addr, final_max_fee],
10631071
)
10641072
)
10651073

@@ -1302,6 +1310,9 @@ def place_order(
13021310
if sl_limit_price is not None and tick_size
13031311
else sl_limit_price
13041312
)
1313+
final_builder_fee = (
1314+
bps_to_chain_units(builder_fee) if builder_fee is not None else None
1315+
)
13051316

13061317
pkg = self._config.deployment.package
13071318

@@ -1325,7 +1336,7 @@ def _send(addr: str) -> dict[str, Any]:
13251336
final_sl_trigger,
13261337
final_sl_limit,
13271338
builder_addr,
1328-
builder_fee,
1339+
final_builder_fee,
13291340
],
13301341
),
13311342
account_override,
@@ -1391,6 +1402,9 @@ def place_twap_order(
13911402
) -> PlaceOrderResult:
13921403
market_addr = get_market_addr(market_name, self._config.deployment.perp_engine_global)
13931404
pkg = self._config.deployment.package
1405+
final_builder_fees = (
1406+
bps_to_chain_units(builder_fees) if builder_fees is not None else None
1407+
)
13941408

13951409
def _send(addr: str) -> dict[str, Any]:
13961410
return self._send_tx(
@@ -1407,7 +1421,7 @@ def _send(addr: str) -> dict[str, Any]:
14071421
twap_frequency_seconds,
14081422
twap_duration_seconds,
14091423
builder_address,
1410-
builder_fees,
1424+
final_builder_fees,
14111425
],
14121426
),
14131427
account_override,
@@ -2077,17 +2091,18 @@ def approve_max_builder_fee(
20772091
self,
20782092
*,
20792093
builder_addr: str,
2080-
max_fee: int,
2094+
max_fee: int | float,
20812095
subaccount_addr: str | None = None,
20822096
) -> dict[str, Any]:
20832097
pkg = self._config.deployment.package
2098+
final_max_fee = bps_to_chain_units(max_fee)
20842099

20852100
def _send(addr: str) -> dict[str, Any]:
20862101
return self._send_tx(
20872102
InputEntryFunctionData(
20882103
function=f"{pkg}::dex_accounts_entry::approve_max_builder_fee_for_subaccount",
20892104
type_arguments=[],
2090-
function_arguments=[addr, builder_addr, max_fee],
2105+
function_arguments=[addr, builder_addr, final_max_fee],
20912106
)
20922107
)
20932108

src/decibel/write/_types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class CancelTpSlOrderArgs(TypedDict):
151151

152152
class ApproveBuilderFeeArgs(TypedDict):
153153
builder_addr: str
154-
max_fee: int
154+
max_fee: int | float
155155
subaccount_addr: NotRequired[str | None]
156156

157157

0 commit comments

Comments
 (0)