Skip to content
This repository was archived by the owner on Jan 19, 2026. It is now read-only.

Commit c1c7a79

Browse files
authored
[EXC-828] Sub account features (#61)
* added arb market * added parent address to GetFundingHistoryRequest * added parent address to GetUserTradesRequest * added sub account support in get_user_account_data * added parent address field to GetPositionRequest * added parent address to GetOrderRequest * corrected GetOrderRequest interface * added sub account support to get user leverage and adjust user leverage method * added parent address to order cancel signature request * updated cancel order methods to support sub account * bumped to v0.1.3 * added parent address to adjust margin * bumped to 0.1.3 --------- Co-authored-by: YameenMalik <yameen@seed.im>
1 parent 10f83ec commit c1c7a79

File tree

4 files changed

+51
-32
lines changed

4 files changed

+51
-32
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "firefly_exchange_client"
3-
version = "0.1.2"
3+
version = "0.1.3"
44
description = "Library to interact with firefly exchange protocol including its off-chain api-gateway and on-chain contracts"
55
readme = "README.md"
66
requires-python = ">=3.8"

src/firefly_exchange_client/client.py

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,14 @@ def create_signed_order(self, params:OrderSignatureRequest):
200200
maker=order["maker"]
201201
)
202202

203-
def create_signed_cancel_order(self,params:OrderSignatureRequest):
203+
def create_signed_cancel_order(self,params:OrderSignatureRequest, parentAddress:str=""):
204204
"""
205205
Creates a cancel order request from provided params and signs it using the private
206206
key of the account
207207
208208
Inputs:
209209
- params (OrderSignatureRequest): parameters to create cancel order with
210+
- parentAddress (str): Only provided by a sub account
210211
211212
Returns:
212213
- OrderSignatureResponse: generated cancel signature
@@ -215,30 +216,32 @@ def create_signed_cancel_order(self,params:OrderSignatureRequest):
215216
signer:OrderSigner = self._get_order_signer(params["symbol"])
216217
order_to_sign = self.create_order_to_sign(params)
217218
hash = signer.get_order_hash(order_to_sign)
218-
return self.create_signed_cancel_orders(params["symbol"],hash)
219+
return self.create_signed_cancel_orders(params["symbol"], hash, parentAddress)
219220
except Exception as e:
220221
return ""
221222

222-
def create_signed_cancel_orders(self,symbol:MARKET_SYMBOLS,order_hash:list):
223+
def create_signed_cancel_orders(self, symbol:MARKET_SYMBOLS, order_hash:list, parentAddress:str=""):
223224
"""
224225
Creates a cancel order from provided params and sign it using the private
225226
key of the account
226227
227228
Inputs:
228-
params (list): a list of order hashes
229-
229+
- params (list): a list of order hashes
230+
- parentAddress (str): only provided by a sub account
230231
Returns:
231232
OrderCancellationRequest: containing symbol, hashes and signature
232233
"""
233234
if type(order_hash)!=list:
234235
order_hash = [order_hash]
236+
235237
order_signer:OrderSigner = self._get_order_signer(symbol)
236238
cancel_hash = order_signer.sign_cancellation_hash(order_hash)
237239
hash_sig = order_signer.sign_hash(cancel_hash,self.account.key.hex(), "01")
238240
return OrderCancellationRequest(
239241
symbol=symbol.value,
240242
hashes=order_hash,
241-
signature=hash_sig
243+
signature=hash_sig,
244+
parentAddress=parentAddress
242245
)
243246

244247
async def post_cancel_order(self,params:OrderCancellationRequest):
@@ -255,22 +258,25 @@ async def post_cancel_order(self,params:OrderCancellationRequest):
255258
{
256259
"symbol": params["symbol"],
257260
"orderHashes":params["hashes"],
258-
"cancelSignature":params["signature"]
261+
"cancelSignature":params["signature"],
262+
"parentAddress": params["parentAddress"],
259263
},
260264
auth_required=True
261265
)
262266

263-
async def cancel_all_open_orders(self,symbol:MARKET_SYMBOLS):
267+
async def cancel_all_open_orders(self,symbol:MARKET_SYMBOLS, parentAddress:str=""):
264268
"""
265269
GETs all open orders for the specified symbol, creates a cancellation request
266270
for all orders and POSTs the cancel order request to Firefly
267271
Inputs:
268-
- symbol(MARKET_SYMBOLS)
272+
- symbol (MARKET_SYMBOLS): Market for which orders are to be cancelled
273+
- parentAddress (str): address of parent account, only provided by sub account
269274
Returns:
270275
- dict: response from orders delete API Firefly
271276
"""
272277
orders = await self.get_orders({
273278
"symbol":symbol,
279+
"parentAddress": parentAddress,
274280
"statuses":[ORDER_STATUS.OPEN, ORDER_STATUS.PARTIAL_FILLED]
275281
})
276282

@@ -279,7 +285,7 @@ async def cancel_all_open_orders(self,symbol:MARKET_SYMBOLS):
279285
hashes.append(i["hash"])
280286

281287
if len(hashes) > 0:
282-
req = self.create_signed_cancel_orders(symbol,hashes)
288+
req = self.create_signed_cancel_orders(symbol, hashes, parentAddress)
283289
return await self.post_cancel_order(req)
284290

285291
return False
@@ -384,39 +390,41 @@ async def withdraw_margin_from_bank(self, amount):
384390

385391
return True;
386392

387-
async def adjust_leverage(self, symbol, leverage):
393+
async def adjust_leverage(self, symbol, leverage, parentAddress:str=""):
388394
"""
389395
Adjusts user leverage to the provided one for their current position on-chain and off-chain.
390396
If a user has no position for the provided symbol, leverage only recorded off-chain
391397
392398
Inputs:
393399
symbol (MARKET_SYMBOLS): market for which to adjust user leverage
394400
leverage (number): new leverage to be set. Must be in base decimals (1,2 etc.)
395-
401+
parentAddress (str): optional, if provided, the leverage of parent is
402+
being adjusted (for sub accounts only)
396403
Returns:
397404
Boolean: true if the leverage is successfully adjusted
398405
"""
399406

400-
user_position = await self.get_user_position({"symbol":symbol})
401-
407+
user_position = await self.get_user_position({"symbol":symbol, "parentAddress": parentAddress})
408+
409+
account_address = Web3.toChecksumAddress(self.account.address if parentAddress == "" else parentAddress)
410+
402411
# implies user has an open position on-chain, perform on-chain leverage update
403412
if(user_position != {}):
404413
perp_contract = self.contracts.get_contract(name="Perpetual", market=symbol.value);
405414
construct_txn = perp_contract.functions.adjustLeverage(
406-
self.account.address,
415+
account_address,
407416
to_wei(leverage, "ether")).buildTransaction({
408417
'from': self.account.address,
409418
'nonce': self.w3.eth.getTransactionCount(self.account.address),
410-
})
411-
419+
})
412420
self._execute_tx(construct_txn)
413421

414422
else:
415423
await self.apis.post(
416424
SERVICE_URLS["USER"]["ADJUST_LEVERAGE"],
417425
{
418426
"symbol": symbol.value,
419-
"address": self.account.address,
427+
"address": account_address,
420428
"leverage": to_wei(leverage, "ether"),
421429
"marginType": MARGIN_TYPE.ISOLATED.value,
422430
},
@@ -425,20 +433,23 @@ async def adjust_leverage(self, symbol, leverage):
425433

426434
return True
427435

428-
async def adjust_margin(self, symbol, operation, amount):
436+
async def adjust_margin(self, symbol, operation, amount, parentAddress:str=""):
429437
"""
430438
Adjusts user's on-chain position by adding or removing the specified amount of margin.
431439
Performs on-chain contract call, the user must have gas tokens
432440
Inputs:
433441
symbol (MARKET_SYMBOLS): market for which to adjust user leverage
434442
operation (ADJUST_MARGIN): ADD/REMOVE adding or removing margin to position
435443
amount (number): amount of margin to be adjusted
436-
444+
parentAddress (str): optional, if provided, the margin of parent is
445+
being adjusted (for sub accounts only)
437446
Returns:
438447
Boolean: true if the margin is adjusted
439448
"""
440449

441-
user_position = await self.get_user_position({"symbol":symbol})
450+
user_position = await self.get_user_position({"symbol":symbol, "parentAddress": parentAddress})
451+
452+
account_address = Web3.toChecksumAddress(self.account.address if parentAddress == "" else parentAddress)
442453

443454
if(user_position == {}):
444455
raise(Exception("User has no open position on market: {}".format(symbol)))
@@ -447,7 +458,7 @@ async def adjust_margin(self, symbol, operation, amount):
447458
on_chain_call = perp_contract.functions.addMargin if operation == ADJUST_MARGIN.ADD else perp_contract.functions.removeMargin
448459

449460
construct_txn = on_chain_call(
450-
self.account.address,
461+
account_address,
451462
to_wei(amount, "ether")).buildTransaction({
452463
'from': self.account.address,
453464
'nonce': self.w3.eth.getTransactionCount(self.account.address),
@@ -746,24 +757,27 @@ async def get_user_trades(self,params:GetUserTradesRequest):
746757
True
747758
)
748759

749-
async def get_user_account_data(self):
760+
async def get_user_account_data(self, parentAddress:str = ""):
750761
"""
751762
Returns user account data.
763+
Inputs:
764+
- parentAddress: an optional field, used by sub accounts to fetch parent account state
752765
"""
753766
return await self.apis.get(
754767
service_url = SERVICE_URLS["USER"]["ACCOUNT"],
768+
query = { "parentAddress": parentAddress },
755769
auth_required = True
756770
)
757771

758-
async def get_user_leverage(self, symbol:MARKET_SYMBOLS):
772+
async def get_user_leverage(self, symbol:MARKET_SYMBOLS, parentAddress:str=""):
759773
"""
760774
Returns user market default leverage.
761775
Inputs:
762776
- symbol(MARKET_SYMBOLS): market symbol to get user market default leverage for.
763777
Returns:
764778
- str: user default leverage
765779
"""
766-
account_data_by_market = (await self.get_user_account_data())["accountDataByMarket"]
780+
account_data_by_market = (await self.get_user_account_data(parentAddress))["accountDataByMarket"]
767781

768782
for i in account_data_by_market:
769783
if symbol.value==i["symbol"]:

src/firefly_exchange_client/enumerations.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class MARKET_SYMBOLS(Enum):
1515
LINK = "LINK-PERP"
1616
MATIC = "MATIC-PERP"
1717
DOGE = "DOGE-PERP"
18+
ARB = "ARB-PERP"
19+
1820

1921
class TIME_IN_FORCE(Enum):
2022
FILL_OR_KILL = "FOK"

src/firefly_exchange_client/interfaces.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class OrderSignatureRequest(RequiredOrderFields):
2929
salt: int # (optional) random number for uniqueness of order. Generated randomly if not provided
3030
expiration: int # (optional) time at which order will expire. Will be set to 1 month if not provided
3131
maker: str # (optional) maker of the order, if not provided the account used to initialize the client will be default maker
32-
32+
3333
class OrderSignatureResponse(RequiredOrderFields):
3434
maker: str
3535
orderSignature: str
@@ -99,6 +99,7 @@ class GetMarketRecentTradesRequest(TypedDict):
9999
class OrderCancelSignatureRequest(TypedDict):
100100
symbol: MARKET_SYMBOLS
101101
hashes: list
102+
parentAddress: str # (optional) should only be provided by a sub account
102103

103104
class OrderCancellationRequest(OrderCancelSignatureRequest):
104105
signature: str
@@ -118,10 +119,8 @@ class GetTransactionHistoryRequest(TypedDict):
118119
pageSize: int # will get only provided number of orders must be <= 50
119120
pageNumber: int # will fetch particular page records. A single page contains 50 records.
120121

121-
class GetPositionRequest(TypedDict):
122-
symbol: MARKET_SYMBOLS # will fetch orders of provided market
123-
pageSize: int # will get only provided number of orders must be <= 50
124-
pageNumber: int # will fetch particular page records. A single page contains 50 records.
122+
class GetPositionRequest(GetTransactionHistoryRequest):
123+
parentAddress : str # (optional) should be provided by sub accounts
125124

126125
class GetUserTradesRequest(TypedDict):
127126
symbol: MARKET_SYMBOLS
@@ -132,14 +131,18 @@ class GetUserTradesRequest(TypedDict):
132131
pageSize: int
133132
pageNumber: int
134133
type: ORDER_TYPE
134+
parentAddress: str # (optional) should be provided by sub account
135135

136136
class GetOrderRequest(GetTransactionHistoryRequest):
137-
statuses:ORDER_STATUS # status of orders to be fetched
137+
statuses:List[ORDER_STATUS] # status of orders to be fetched
138+
parentAddress : str # (optional) should be provided by sub accounts
138139

139140
class GetFundingHistoryRequest(TypedDict):
140141
symbol: MARKET_SYMBOLS # will fetch orders of provided market
141142
pageSize: int # will get only provided number of orders must be <= 50
142143
cursor: int # will fetch particular page records. A single page contains 50 records.
144+
parentAddress: str # (optional) should be provided by a sub account
145+
143146

144147
class FundingHistoryResponse(TypedDict):
145148
id: int # unique id

0 commit comments

Comments
 (0)