### Install derive_action_signing Package Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Use pip to install the derive_action_signing package. ```bash pip install derive_action_signing ``` -------------------------------- ### Sign an Order with derive_action_signing Source: https://github.com/derivexyz/v2-action-signing-python/blob/master/README.md Use this snippet to sign an order. Ensure you have installed the package using `pip install derive_action_signing`. The `owner` address should be the Derive wallet address, not your original EOA. ```python from web3 import Web3 from derive_action_signing import SignedAction, TradeModuleData, utils session_key_wallet = Web3().eth.account.from_key("0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd") action = SignedAction( subaccount_id=30769, # The Derive wallet address (not "owner") of account. This is NOT your original EOA, but the smart contract wallet on the Derive Chain. To find it in the website go to Home -> Developers -> "Derive Wallet". owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=TRADE_MODULE_ADDRESS, # from Protocol Constants table in docs.lyra.finance module_data=TradeModuleData( asset=instrument_ticker["base_asset_address"], sub_id=int(instrument_ticker["base_asset_sub_id"]), limit_price=Decimal("100"), amount=Decimal("1"), max_fee=Decimal("1000"), recipient_id=30769, is_bid=True, ), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, # from Protocol Constants table in docs.derive.xyz ACTION_TYPEHASH=ACTION_TYPEHASH, # from Protocol Constants table in docs.derive.xyz ) action.sign(session_key_wallet.key) ``` -------------------------------- ### Sign and Submit an RFQ Quote via REST API Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt This example demonstrates signing a multi-leg quote response for an incoming RFQ using `RFQQuoteModuleData`. It involves fetching RFQ details, constructing quote legs, signing the action, and submitting it via the REST API. ```python import requests from web3 import Web3 from decimal import Decimal from typing import List from derive_action_signing import SignedAction, RFQQuoteDetails, RFQQuoteModuleData, utils SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" SUBACCOUNT_ID = 30769 DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105" ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17" RFQ_MODULE_ADDRESS = "0x4E4DD8Be1e461913D9A5DBC4B830e67a8694ebCa" web3_client = Web3() session_key_wallet = web3_client.eth.account.from_key(SESSION_KEY_PRIVATE_KEY) # Poll open RFQs rfq = requests.post( "https://api-demo.lyra.finance/private/poll_rfqs", json={"subaccount_id": SUBACCOUNT_ID, "status": "open"}, headers={ **utils.sign_rest_auth_header(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", }, ).json()["result"]["rfqs"][0] global_direction = "sell" rfq_legs: List[RFQQuoteDetails] = [] for leg in rfq["legs"]: instrument = requests.post( "https://api-demo.finance/public/get_instrument", json={"instrument_name": leg["instrument_name"]}, headers={"accept": "application/json", "content-type": "application/json"}, ).json()["result"] rfq_legs.append(RFQQuoteDetails( instrument_name=instrument["instrument_name"], direction=leg["direction"], asset_address=instrument["base_asset_address"], sub_id=int(instrument["base_asset_sub_id"]), price=Decimal("100"), # replace with your quoted price amount=Decimal(leg["amount"]), )) action = SignedAction( subaccount_id=SUBACCOUNT_ID, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=RFQ_MODULE_ADDRESS, module_data=RFQQuoteModuleData( global_direction=global_direction, max_fee=Decimal("123"), legs=rfq_legs, ), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) action.sign(session_key_wallet.key) response = requests.post( "https://api-demo.lyra.finance/private/send_quote", json={**action.to_json(), "label": "", "mmp": False, "rfq_id": rfq["rfq_id"]}, headers={ **utils.sign_rest_auth_header(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", }, ) print(response.json()) ``` -------------------------------- ### Get Current UTC Timestamp in Milliseconds Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Retrieves the current UTC timestamp in milliseconds. ```python from derive_action_signing import utils # Current UTC timestamp in milliseconds ts = utils.utc_now_ms() print(ts) # e.g. 1695836058725 ``` -------------------------------- ### Sign and Submit a Trade Order via WebSocket Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt This snippet shows how to fetch instrument details, create a signed trade action, and submit it via WebSocket. Ensure you have established a WebSocket connection and logged in before sending the order. ```python instrument_ticker = requests.post( "https://api-demo.lyra.finance/public/get_instruments", json={"expired": False, "instrument_type": "option", "currency": "ETH"}, headers={"accept": "application/json", "content-type": "application/json"}, ).json()["result"][0] action = SignedAction( subaccount_id=SUBACCOUNT_ID, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=TRADE_MODULE_ADDRESS, module_data=TradeModuleData( asset_address=instrument_ticker["base_asset_address"], sub_id=int(instrument_ticker["base_asset_sub_id"]), limit_price=Decimal("100"), amount=Decimal("1"), max_fee=Decimal("1000"), recipient_id=SUBACCOUNT_ID, is_bid=True, ), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) action.sign(session_key_wallet.key) # Submit via WebSocket ws = create_connection(WEBSOCKET_URL) login_id = str(utils.utc_now_ms()) ws.send(json.dumps({ "method": "public/login", "params": utils.sign_ws_login(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "id": login_id, })) # wait for login ack, then send order order_id = str(utils.utc_now_ms()) ws.send(json.dumps({ "method": "private/order", "params": { "instrument_name": instrument_ticker["instrument_name"], "direction": "buy", "order_type": "limit", "mmp": False, "time_in_force": "gtc", **action.to_json(), }, "id": order_id, })) ``` -------------------------------- ### Create and Sign a Trade Action with SignedAction Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Construct a SignedAction for a limit order, sign it with a private key, and validate the signature. The owner field must be the Derive smart contract wallet address. ```python from web3 import Web3 from decimal import Decimal from derive_action_signing import SignedAction, TradeModuleData, utils DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105" ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17" TRADE_MODULE_ADDRESS = "0x87F2863866D85E3192a35A73b388BD625D83f2be" SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" web3_client = Web3() session_key_wallet = web3_client.eth.account.from_key(SESSION_KEY_PRIVATE_KEY) action = SignedAction( subaccount_id=30769, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, # never expires nonce=utils.get_action_nonce(), module_address=TRADE_MODULE_ADDRESS, module_data=TradeModuleData( asset_address="0xabc123...", # base_asset_address from get_instruments sub_id=12345, # base_asset_sub_id from get_instruments limit_price=Decimal("100"), amount=Decimal("1"), max_fee=Decimal("1000"), recipient_id=30769, is_bid=True, ), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) signature = action.sign(session_key_wallet.key) # returns hex signature string action.validate_signature() # raises ValueError if invalid print(action.to_json()) # { # "subaccount_id": 30769, # "nonce": 1695836058725001, # "signer": "0x...", # "signature_expiry_sec": 2147483647, # "signature": "0x...", # "limit_price": "100", # "amount": "1", # "max_fee": "1000" # } ``` -------------------------------- ### Sign RFQ Quote Execution Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt This snippet demonstrates the complete process of signing and executing an RFQ quote. It includes fetching instrument data, preparing RFQ module data, sending an RFQ request, polling for quotes, selecting a suitable quote, updating leg prices, creating a signed action, and finally executing the quote. ```python import time, json, requests from web3 import Web3 from decimal import Decimal from derive_action_signing import SignedAction, RFQQuoteDetails, RFQExecuteModuleData, utils SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" SUBACCOUNT_ID = 30769 DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105" ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17" RFQ_MODULE_ADDRESS = "0x4E4DD8Be1e461913D9A5DBC4B830e67a8694ebCa" web3_client = Web3() session_key_wallet = web3_client.eth.account.from_key(SESSION_KEY_PRIVATE_KEY) first = requests.post("https://api-demo.lyra.finance/public/get_instrument", json={"instrument_name": "ETH-20240329-3200-C"}, headers={"accept": "application/json", "content-type": "application/json"}, ).json()["result"] second = requests.post("https://api-demo.lyra.finance/public/get_instrument", json={"instrument_name": "ETH-20240329-3200-P"}, headers={"accept": "application/json", "content-type": "application/json"}, ).json()["result"] rfq_module_data = RFQExecuteModuleData( global_direction="buy", max_fee=Decimal("1000"), legs=[ RFQQuoteDetails( instrument_name=first["instrument_name"], direction="sell", asset_address=first["base_asset_address"], sub_id=int(first["base_asset_sub_id"]), price=Decimal("0"), amount=Decimal("1"), ), RFQQuoteDetails( instrument_name=second["instrument_name"], direction="buy", asset_address=second["base_asset_address"], sub_id=int(second["base_asset_sub_id"]), price=Decimal("0"), amount=Decimal("2"), ), ], ) # Send RFQ request (no signing needed yet) requests.post( "https://api-demo.lyra.finance/private/send_rfq", json={"subaccount_id": SUBACCOUNT_ID, **rfq_module_data.to_rfq_json()}, headers={ **utils.sign_rest_auth_header(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", }, ) time.sleep(5) # wait for quotes quotes = requests.post( "https://api-demo.lyra.finance/private/poll_quotes", json={"subaccount_id": SUBACCOUNT_ID, "status": "open"}, headers={ **utils.sign_rest_auth_header(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", }, ).json()["result"]["quotes"] # Pick a quote with opposite direction and fill in prices quote = next(q for q in quotes if q["direction"] != rfq_module_data.global_direction) for i, leg in enumerate(rfq_module_data.legs): leg.price = Decimal(quote["legs"][i]["price"]) action = SignedAction( subaccount_id=SUBACCOUNT_ID, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=RFQ_MODULE_ADDRESS, module_data=rfq_module_data, DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) action.sign(session_key_wallet.key) response = requests.post( "https://api-demo.lyra.finance/private/execute_quote", json={**action.to_json(), "label": "", "rfq_id": quote["rfq_id"], "quote_id": quote["quote_id"]}, headers={ **utils.sign_rest_auth_header(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", }, ) print(json.dumps(response.json(), indent=4)) ``` -------------------------------- ### Run Tests with Disabled Plugin Autoload Source: https://github.com/derivexyz/v2-action-signing-python/blob/master/README.md If you encounter issues with eth-typing compatibility when running tests, use this command. This prevents loading of web3's pytest plugin which has compatibility issues with eth-typing v5. ```bash PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 pytest ``` -------------------------------- ### Utility Functions: get_action_nonce and decimal_to_big_int Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Provides utility functions for generating replay-safe nonces and converting Decimal values to big integers suitable for ABI encoding. ```APIDOC ## `get_action_nonce` and `decimal_to_big_int` — Utility Functions `get_action_nonce()` creates replay-safe nonces by combining the current UTC timestamp in milliseconds with up to a 3-digit suffix (random or fixed via `nonce_iter`). Pass `nonce_iter=None` to use a random suffix, or pass a specific integer (0–999) to fix it (useful for generating sequential nonces in the same millisecond). `decimal_to_big_int()` scales a `Decimal` value by 10^18 for ABI encoding, and raises `ValueError` if the result falls outside the int256 range. ```python from decimal import Decimal from derive_action_signing import utils # Random 3-digit suffix (default) nonce = utils.get_action_nonce() print(nonce) # e.g. 1695836058725042 # Fixed suffix – useful for generating two nonces that are guaranteed to differ nonce_maker = utils.get_action_nonce(nonce_iter=0) # ends in ...000 nonce_taker = utils.get_action_nonce(nonce_iter=1) # ends in ...001 # Explicit random suffix nonce_random = utils.get_action_nonce(nonce_iter=None) # Scale Decimal → uint256-compatible integer price_int = utils.decimal_to_big_int(Decimal("1234.56")) print(price_int) # 1234560000000000000000 # Out-of-range values raise ValueError try: utils.decimal_to_big_int(Decimal("2") ** 256) except ValueError as e: print(e) # resulting integer value must be between ... # Current UTC timestamp in milliseconds ts = utils.utc_now_ms() print(ts) # e.g. 1695836058725 # Useful constants print(utils.MAX_INT_32) # 2147483647 – use as signature_expiry_sec for non-expiring sigs print(utils.MAX_INT_256) # 2**255 - 1 print(utils.MIN_INT_256) # -(2**255) ``` ``` -------------------------------- ### Sign REST API Authentication Headers Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Generates the necessary authentication headers for Derive REST endpoints. Ensure you have the web3 client, smart contract wallet address, and session key private key initialized. ```python import requests from web3 import Web3 from derive_action_signing import utils web3_client = Web3() SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" auth_headers = utils.sign_rest_auth_header( web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY ) # {"X-LYRAWALLET": "0x...", "X-LYRATIMESTAMP": "1695836058725", "X-LYRASIGNATURE": "0x..."} response = requests.post( "https://api-demo.lyra.finance/private/get_subaccount", json={"subaccount_id": 30769}, headers={**auth_headers, "accept": "application/json", "content-type": "application/json"}, ) print(response.json()) ``` -------------------------------- ### Generate Replay-Safe Nonces Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Creates replay-safe nonces by combining the current UTC timestamp with an optional 3-digit suffix. Use `nonce_iter=None` for a random suffix, or an integer (0-999) to fix it for sequential generation within the same millisecond. ```python from decimal import Decimal from derive_action_signing import utils # Random 3-digit suffix (default) nonce = utils.get_action_nonce() print(nonce) # e.g. 1695836058725042 # Fixed suffix – useful for generating two nonces that are guaranteed to differ nonce_maker = utils.get_action_nonce(nonce_iter=0) # ends in ...000 nonce_taker = utils.get_action_nonce(nonce_iter=1) # ends in ...001 # Explicit random suffix nonce_random = utils.get_action_nonce(nonce_iter=None) ``` -------------------------------- ### Sign and Send ERC20 Collateral Transfer Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt This snippet demonstrates how to construct, sign, and send signed actions for an ERC20 collateral transfer between two subaccounts. It includes setting up transfer details, creating sender and recipient actions, and making a POST request to the transfer endpoint. ```python import requests, json from web3 import Web3 from decimal import Decimal from derive_action_signing import ( SignedAction, TransferERC20Details, SenderTransferERC20ModuleData, RecipientTransferERC20ModuleData, utils, ) SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" FROM_SUBACCOUNT_ID, TO_SUBACCOUNT_ID = 30769, 31049 DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105" ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17" TRANSFER_ERC20_MODULE_ADDRESS = "0x0CFC1a4a90741aB242cAfaCD798b409E12e68926" CASH_ASSET_ADDRESS = "0x6caf294DaC985ff653d5aE75b4FF8E0A66025928" web3_client = Web3() session_key_wallet = web3_client.eth.account.from_key(SESSION_KEY_PRIVATE_KEY) transfer_details = TransferERC20Details( base_address=CASH_ASSET_ADDRESS, sub_id=0, # always 0 for base assets amount=Decimal("123"), ) sender_action = SignedAction( subaccount_id=FROM_SUBACCOUNT_ID, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=TRANSFER_ERC20_MODULE_ADDRESS, module_data=SenderTransferERC20ModuleData( to_subaccount_id=TO_SUBACCOUNT_ID, transfers=[transfer_details], ), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) sender_action.sign(session_key_wallet.key) recipient_action = SignedAction( subaccount_id=TO_SUBACCOUNT_ID, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=TRANSFER_ERC20_MODULE_ADDRESS, module_data=RecipientTransferERC20ModuleData(), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) recipient_action.sign(session_key_wallet.key) response = requests.post( "https://api-demo.lyra.finance/private/transfer_erc20", json={ "subaccount_id": sender_action.subaccount_id, "recipient_subaccount_id": recipient_action.subaccount_id, "sender_details": { "nonce": sender_action.nonce, "signature": sender_action.signature, "signature_expiry_sec": sender_action.signature_expiry_sec, "signer": sender_action.signer, }, "recipient_details": { "nonce": recipient_action.nonce, "signature": recipient_action.signature, "signature_expiry_sec": recipient_action.signature_expiry_sec, "signer": recipient_action.signer, }, "transfer": { "address": CASH_ASSET_ADDRESS, "amount": str(transfer_details.amount), "sub_id": str(transfer_details.sub_id), }, }, headers={ **utils.sign_rest_auth_header(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", }, ) print(json.dumps(response.json(), indent=4)) ``` -------------------------------- ### Derive Protocol Constants Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Provides useful constants for interacting with the Derive protocol, including maximum and minimum values for integer types. ```python from derive_action_signing import utils # Useful constants print(utils.MAX_INT_32) # 2147483647 – use as signature_expiry_sec for non-expiring sigs print(utils.MAX_INT_256) # 2**255 - 1 print(utils.MIN_INT_256) # -(2**255) ``` -------------------------------- ### Atomically Transfer Multiple Derivative Positions Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Use MakerTransferPositionsModuleData and TakerTransferPositionsModuleData to atomically transfer multiple derivative positions in a single /private/transfer_positions call. Ensure the correct RFQ_MODULE_ADDRESS is used and global_direction is opposite for maker and taker. ```python import requests, json from web3 import Web3 from decimal import Decimal from derive_action_signing import ( SignedAction, TransferPositionsDetails, MakerTransferPositionsModuleData, TakerTransferPositionsModuleData, utils, ) SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" FROM_SUBACCOUNT_ID, TO_SUBACCOUNT_ID = 30769, 31049 DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105" ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17" RFQ_MODULE_ADDRESS = "0x4E4DD8Be1e461913D9A5DBC4B830e67a8694ebCa" web3_client = Web3() session_key_wallet = web3_client.eth.account.from_key(SESSION_KEY_PRIVATE_KEY) get_instr = lambda name: requests.post( "https://api-demo.lyra.finance/public/get_instrument", json={"instrument_name": name}, headers={"accept": "application/json", "content-type": "application/json"}, ).json()["result"] first = get_instr("ETH-20240329-1600-C") second = get_instr("ETH-20240329-1600-P") positions = [ TransferPositionsDetails( instrument_name=first["instrument_name"], direction="sell", asset_address=first["base_asset_address"], sub_id=int(first["base_asset_sub_id"]), price=Decimal("75"), amount=Decimal("0.1"), ), TransferPositionsDetails( instrument_name=second["instrument_name"], direction="buy", asset_address=second["base_asset_address"], sub_id=int(second["base_asset_sub_id"]), price=Decimal("50"), amount=Decimal("0.2"), ), ] sender_action = SignedAction( subaccount_id=FROM_SUBACCOUNT_ID, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=RFQ_MODULE_ADDRESS, module_data=MakerTransferPositionsModuleData(global_direction="buy", positions=positions), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) recipient_action = SignedAction( subaccount_id=TO_SUBACCOUNT_ID, owner=SMART_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=utils.get_action_nonce(), module_address=RFQ_MODULE_ADDRESS, module_data=TakerTransferPositionsModuleData(global_direction="sell", positions=positions), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) sender_action.sign(session_key_wallet.key) recipient_action.sign(session_key_wallet.key) response = requests.post( "https://api-demo.lyra.finance/private/transfer_positions", json={ "wallet": SMART_CONTRACT_WALLET_ADDRESS, "maker_params": sender_action.to_json(), "taker_params": recipient_action.to_json(), }, headers={ **utils.sign_rest_auth_header(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", }, ) print(json.dumps(response.json(), indent=4)) ``` -------------------------------- ### sign_rest_auth_header Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Generates the three authentication headers required by all authenticated Derive REST endpoints. Signs the current UTC timestamp with the session key and returns a dict with X-LYRAWALLET, X-LYRATIMESTAMP, and X-LYRASIGNATURE that can be spread into a requests call. ```APIDOC ## `sign_rest_auth_header` — REST API Authentication Headers Generates the three authentication headers required by all authenticated Derive REST endpoints. Signs the current UTC timestamp with the session key and returns a dict with `X-LYRAWALLET`, `X-LYRATIMESTAMP`, and `X-LYRASIGNATURE` that can be spread into a `requests` call. ```python import requests from web3 import Web3 from derive_action_signing import utils web3_client = Web3() SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" auth_headers = utils.sign_rest_auth_header( web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY ) # {"X-LYRAWALLET": "0x...", "X-LYRATIMESTAMP": "1695836058725", "X-LYRASIGNATURE": "0x..."} response = requests.post( "https://api-demo.lyra.finance/private/get_subaccount", json={"subaccount_id": 30769}, headers={**auth_headers, "accept": "application/json", "content-type": "application/json"}, ) print(response.json()) ``` ``` -------------------------------- ### Encode Limit Order with TradeModuleData Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt TradeModuleData encodes a limit order for a given instrument, handling Decimal to integer scaling for ABI encoding. Use is_bid=True for buy orders and False for sell orders. ```python import requests import json from web3 import Web3 from decimal import Decimal from websocket import create_connection from derive_action_signing import SignedAction, TradeModuleData, utils SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" SUBACCOUNT_ID = 30769 DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105" ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17" TRADE_MODULE_ADDRESS = "0x87F2863866D85E3192a35A73b388BD625D83f2be" WEBSOCKET_URL = "wss://api-demo.lyra.finance/ws" web3_client = Web3() session_key_wallet = web3_client.eth.account.from_key(SESSION_KEY_PRIVATE_KEY) ``` -------------------------------- ### Authenticate WebSocket Connection Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Generates a login payload for WebSocket authentication by signing the current UTC timestamp. This payload is used with the public/login WebSocket method. ```python from web3 import Web3 from websocket import create_connection import json from derive_action_signing import utils web3_client = Web3() SMART_CONTRACT_WALLET_ADDRESS = "0x8772185a1516f0d61fC1c2524926BfC69F95d698" SESSION_KEY_PRIVATE_KEY = "0x2ae8be44db8a590d20bffbe3b6872df9b569147d3bf6801a35a28281a4816bbd" ws = create_connection("wss://api-demo.lyra.finance/ws") login_id = str(utils.utc_now_ms()) ws.send(json.dumps({ "method": "public/login", "params": utils.sign_ws_login(web3_client, SMART_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), # returns: {"wallet": "0x...", "timestamp": "1695836058725", "signature": "0x..."} "id": login_id, })) response = json.loads(ws.recv()) # {"id": "...", "result": {"token": "..."}} ``` -------------------------------- ### Perform Single Position Transfer with Python SDK Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Use this snippet to transfer a single derivative position between two subaccounts. Ensure you have the necessary imports and constants defined. The `position_amount` field automatically determines the `is_bid` logic. ```python import requests, json from decimal import Decimal, ROUND_HALF_UP from web3 import Web3 from derive_action_signing import ( SignedAction, MakerTransferPositionModuleData, TakerTransferPositionModuleData, utils, ) DERIVE_CONTRACT_WALLET_ADDRESS = "0xeda0656dab4094C7Dc12F8F12AF75B5B3Af4e776" SESSION_KEY_PRIVATE_KEY = "0x83ee63dc6655509aabce0f7e501a31c511195e61e9d0e9917f0a55fd06041a66" FROM_SUBACCOUNT_ID, TO_SUBACCOUNT_ID = 137402, 137404 DOMAIN_SEPARATOR = "0x9bcf4dc06df5d8bf23af818d5716491b995020f377d3b7b64c29ed14e3dd1105" ACTION_TYPEHASH = "0x4d7a9f27c403ff9c0f19bce61d76d82f9aa29f8d6d4b0c5474607d9770d1af17" TRADE_MODULE_ADDRESS = "0x87F2863866D85E3192a35A73b388BD625D83f2be" web3_client = Web3() session_key_wallet = web3_client.eth.account.from_key(SESSION_KEY_PRIVATE_KEY) auth_headers = { **utils.sign_rest_auth_header(web3_client, DERIVE_CONTRACT_WALLET_ADDRESS, SESSION_KEY_PRIVATE_KEY), "accept": "application/json", "content-type": "application/json", } # Get existing position position = requests.post( "https://api-demo.lyra.finance/private/get_positions", json={"subaccount_id": FROM_SUBACCOUNT_ID}, headers=auth_headers, ).json()["result"]["positions"][0] instrument = requests.post( "https://api-demo.lyra.finance/public/get_instruments", json={"currency": "ETH", "instrument_type": position["instrument_type"], "expired": False}, headers={"accept": "application/json", "content-type": "application/json"}, ).json()["result"] instrument = next(i for i in instrument if i["instrument_name"] == position["instrument_name"]) original_amount = Decimal(position["amount"]) transfer_amount = abs(original_amount).quantize(Decimal("0.1"), rounding=ROUND_HALF_UP) transfer_price = abs(Decimal(position["mark_value"])).quantize(Decimal("0.1"), rounding=ROUND_HALF_UP) base_nonce = utils.get_action_nonce() maker_action = SignedAction( subaccount_id=FROM_SUBACCOUNT_ID, owner=DERIVE_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=base_nonce, module_address=TRADE_MODULE_ADDRESS, module_data=MakerTransferPositionModuleData( asset_address=instrument["base_asset_address"], sub_id=int(instrument["base_asset_sub_id"]), limit_price=transfer_price, amount=transfer_amount, recipient_id=FROM_SUBACCOUNT_ID, position_amount=original_amount, # drives is_bid logic automatically ), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) taker_action = SignedAction( subaccount_id=TO_SUBACCOUNT_ID, owner=DERIVE_CONTRACT_WALLET_ADDRESS, signer=session_key_wallet.address, signature_expiry_sec=utils.MAX_INT_32, nonce=base_nonce + 1, module_address=TRADE_MODULE_ADDRESS, module_data=TakerTransferPositionModuleData( asset_address=instrument["base_asset_address"], sub_id=int(instrument["base_asset_sub_id"]), limit_price=transfer_price, amount=transfer_amount, recipient_id=TO_SUBACCOUNT_ID, position_amount=original_amount, ), DOMAIN_SEPARATOR=DOMAIN_SEPARATOR, ACTION_TYPEHASH=ACTION_TYPEHASH, ) maker_action.sign(session_key_wallet.key) taker_action.sign(session_key_wallet.key) response = requests.post( "https://api-demo.lyra.finance/private/transfer_position", json={ "wallet": DERIVE_CONTRACT_WALLET_ADDRESS, "maker_params": {"direction": maker_action.module_data.get_direction(), "instrument_name": instrument["instrument_name"], **maker_action.to_json()}, "taker_params": {"direction": taker_action.module_data.get_direction(), "instrument_name": instrument["instrument_name"], **taker_action.to_json()}, }, headers=auth_headers, ) print(json.dumps(response.json(), indent=4)) ``` -------------------------------- ### Convert Decimal to Big Integer for ABI Encoding Source: https://context7.com/derivexyz/v2-action-signing-python/llms.txt Scales a `Decimal` value by 10^18 for ABI encoding. Raises `ValueError` if the resulting integer exceeds the int256 range. ```python from decimal import Decimal from derive_action_signing import utils # Scale Decimal → uint256-compatible integer price_int = utils.decimal_to_big_int(Decimal("1234.56")) print(price_int) # 1234560000000000000000 # Out-of-range values raise ValueError try: utils.decimal_to_big_int(Decimal("2") ** 256) except ValueError as e: print(e) # resulting integer value must be between ... ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.