Skip to content

Commit 7745a60

Browse files
Always derive wallet address from private key (#35)
* Always derive wallet address from private key * linting * linting * linting * distinguish between owner and signer wallets * fixes * chore: bump SDK version to 2.0.6.1 --------- Co-authored-by: GitHub Action <action@github.com>
1 parent 2b07947 commit 7745a60

11 files changed

Lines changed: 71 additions & 54 deletions

File tree

.env.example

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ REYA_WS_URL=
55
PRIVATE_KEY=
66
ACCOUNT_ID=
77
CHAIN_ID= # 1729 mainnet and 89346162 cronos
8-
WALLET_ADDRESS=
8+
9+
# The wallet address that owns ACCOUNT_ID
10+
OWNER_WALLET_ADDRESS=
911

1012
### Reya Cronos (testnet) example
1113
ACCOUNT_ID=replaceme
1214
PRIVATE_KEY=replacemereplacemereplacemereplacemereplacemereplacemereplaceme
1315
CHAIN_ID=89346162
1416
REYA_WS_URL="wss://websocket-testnet.reya.xyz/"
15-
WALLET_ADDRESS=replaceme
17+
OWMNER_WALLET_ADDRESS=replaceme
1618

1719
### Reya Network (mainnet) example
1820
ACCOUNT_ID=replaceme
1921
PRIVATE_KEY=replacemereplacemereplacemereplacemereplacemereplacemereplaceme
2022
CHAIN_ID=1729
2123
REYA_WS_URL="wss://ws.reya.xyz/"
22-
WALLET_ADDRESS=replaceme
24+
OWNER_WALLET_ADDRESS=replaceme

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,17 @@ PRIVATE_KEY=your_private_key
167167
CHAIN_ID=1729 # Use 89346162 for testnet
168168
REYA_WS_URL=wss://ws.reya.xyz/ # Use wss://websocket-testnet.reya.xyz/ for testnet
169169
REYA_API_BASE_URL=https://api.reya.xyz/v2 # Use https://api-test.reya.xyz/v2 for testnet
170-
WALLET_ADDRESS=your_wallet_address
170+
OWNER_WALLET_ADDRESS=your_wallet_address # Required: wallet address for data queries
171171
```
172+
173+
### Signer vs Owner Wallet
174+
175+
The SDK distinguishes between two wallet concepts:
176+
177+
- **Signer Wallet**: The wallet that signs transactions and orders (derived from `PRIVATE_KEY`)
178+
- **Owner Wallet**: The wallet whose data you want to query (positions, orders, balances, etc.)
179+
180+
In most cases, these are the same wallet (set `OWNER_WALLET_ADDRESS` to the address derived from your `PRIVATE_KEY`). However, in Reya DEX, one address can trade on behalf of another wallet. The `OWNER_WALLET_ADDRESS` is used for all wallet data query methods (`get_positions()`, `get_open_orders()`, etc.), while order signatures always use the signer wallet address.
172181
### Resource-Based API Structure
173182

174183
#### REST API Structure

examples/rest_api/account_info.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ async def main():
1818
load_dotenv()
1919

2020
async with ReyaTradingClient() as client:
21-
if not client.wallet_address:
21+
if not client.owner_wallet_address:
2222
print("Error: No wallet address found in environment variables.")
2323
print("Please set either WALLET_ADDRESS or PRIVATE_KEY in your .env file.")
2424
return
2525

26-
print(f"Using wallet address: {client.wallet_address}")
26+
print(f"Using wallet address: {client.owner_wallet_address}")
2727

2828
print("\n--- Getting accounts ---")
2929

examples/rest_api/markets_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ async def main():
3232

3333
if config:
3434
print(f"\n--- Getting trades for market {symbol} ---")
35-
trades = await client.wallet.get_wallet_perp_executions(address=client.wallet_address or "")
35+
trades = await client.wallet.get_wallet_perp_executions(address=client.owner_wallet_address or "")
3636
print(f"Market trades: {trades}")
3737

3838
print(f"\n--- Getting trackers for market {symbol} ---")

examples/rest_api/order_entry.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ async def run_order_retrieval_test(client: ReyaTradingClient):
216216

217217
# Get trades
218218
logger.info("Retrieving trades...")
219-
trades = await client.wallet.get_wallet_perp_executions(address=client.wallet_address or "")
219+
trades = await client.wallet.get_wallet_perp_executions(address=client.owner_wallet_address or "")
220220
logger.info(f"📊 Found {len(trades.data)} trades")
221221

222222
# Get open orders
@@ -253,7 +253,7 @@ async def main():
253253
logger.info(f" Account ID: {client.config.account_id}")
254254
logger.info(f" Chain ID: {client.config.chain_id}")
255255
logger.info(f" API URL: {client.config.api_url}")
256-
logger.info(f" Wallet: {client.wallet_address}")
256+
logger.info(f" Wallet: {client.owner_wallet_address}")
257257

258258
await client.start()
259259

examples/rest_api/wallet_example.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ async def main():
3333
# Create a client instance with configuration from environment variables
3434
async with ReyaTradingClient() as client:
3535
# Check if we have a wallet address (either directly or derived from private key)
36-
if not client.wallet_address:
36+
if not client.owner_wallet_address:
3737
print("Error: No wallet address found in environment variables.")
3838
print("Please set either WALLET_ADDRESS or PRIVATE_KEY in your .env file.")
3939
return
4040

4141
# Show the wallet address we're using
42-
print(f"Using wallet address: {client.wallet_address}")
42+
print(f"Using wallet address: {client.owner_wallet_address}")
4343

4444
# Get open orders for the wallet
4545
print("\n--- Getting open orders ---")

examples/websocket/wallet_monitoring.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- PRIVATE_KEY: Your Ethereum private key
77
- ACCOUNT_ID: Your Reya account ID
88
- CHAIN_ID: The chain ID (1729 for mainnet, 89346162 for testnet)
9-
- WALLET_ADDRESS: The wallet address to monitor
9+
- OWNER_WALLET_ADDRESS: The owner wallet address to monitor (required)
1010
"""
1111

1212
from typing import Any
@@ -38,10 +38,10 @@ def on_open(ws):
3838
"""Handle WebSocket connection open event."""
3939
logger.info("Connection established, subscribing to wallet data")
4040

41-
# Get wallet address from environment
42-
wallet_address = os.environ.get("WALLET_ADDRESS")
41+
# Get owner wallet address from environment
42+
wallet_address = os.environ.get("OWNER_WALLET_ADDRESS")
4343
if not wallet_address:
44-
logger.error("WALLET_ADDRESS environment variable not set")
44+
logger.error("OWNER_WALLET_ADDRESS environment variable is required")
4545
return
4646

4747
logger.info(f"Monitoring wallet: {wallet_address}")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "reya-python-sdk"
3-
version = "2.0.6.0"
3+
version = "2.0.6.1"
44
description = "SDK for interacting with Reya Labs APIs"
55
authors = [
66
{name = "Reya Labs"}

sdk/reya_rest_api/auth/signatures.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ def __init__(self, config: TradingConfig):
3232
if not self._private_key:
3333
raise ValueError("Private key is required for signing")
3434

35-
# Calculate public address from private key
36-
self._public_address: str = str(Account.from_key(self._private_key).address)
35+
# Calculate signer wallet address from private key
36+
self._signer_wallet_address: str = str(Account.from_key(self._private_key).address)
3737

3838
@property
39-
def public_address(self) -> str:
40-
"""Get the public address derived from the private key."""
41-
return self._public_address
39+
def signer_wallet_address(self) -> str:
40+
"""Get the signer wallet address derived from the private key."""
41+
return self._signer_wallet_address
4242

4343
def scale(self, decimals: int):
4444
"""Returns a function that scales a number (str, int, float, or Decimal) to an integer."""
@@ -160,7 +160,7 @@ def sign_raw_order(
160160
"counterpartyAccountIds": counterparty_account_ids,
161161
"orderType": order_type,
162162
"inputs": inputs,
163-
"signer": self._public_address,
163+
"signer": self._signer_wallet_address,
164164
"nonce": nonce,
165165
},
166166
}

sdk/reya_rest_api/client.py

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ def __init__(
6767
api_url: Optional[str] = None,
6868
chain_id: Optional[int] = None,
6969
account_id: Optional[int] = None,
70-
wallet_address: Optional[str] = None,
7170
):
7271
"""
7372
Initialize the Reya Trading client.
@@ -103,8 +102,6 @@ def __init__(
103102
self._config.chain_id = chain_id
104103
if account_id:
105104
self._config.account_id = account_id
106-
if wallet_address:
107-
self._config.wallet_address = wallet_address
108105

109106
# Create signature generator
110107
self._signature_generator = SignatureGenerator(self._config)
@@ -175,14 +172,19 @@ def config(self) -> TradingConfig:
175172
return self._config
176173

177174
@property
178-
def wallet_address(self) -> Optional[str]:
179-
"""Get the wallet address from config or signature generator."""
180-
# First check if wallet address is directly provided in config
181-
if self._config.wallet_address:
182-
return self._config.wallet_address
175+
def signer_wallet_address(self) -> str:
176+
"""Get the signer wallet address (derived from private key)."""
177+
return self._signature_generator.signer_wallet_address
183178

184-
# Otherwise derive it from private key if available
185-
return self._signature_generator.public_address if self._signature_generator else None
179+
@property
180+
def owner_wallet_address(self) -> str:
181+
"""
182+
Get the owner wallet address for querying wallet data.
183+
184+
Wallet that owns ACCOUNT_ID, the signer_wallet will either be the same as owner_wallet_address, or a wallet
185+
that was given permissions to trade on behalf ot he owner_wallet_address
186+
"""
187+
return self._config.owner_wallet_address
186188

187189
async def create_limit_order(self, params: LimitOrderParameters) -> CreateOrderResponse:
188190
"""
@@ -254,8 +256,6 @@ async def create_limit_order(self, params: LimitOrderParameters) -> CreateOrderR
254256
# Build the order request
255257
if self.config.account_id is None:
256258
raise ValueError("Account ID is required for order creation")
257-
if self.config.wallet_address is None:
258-
raise ValueError("Wallet address is required for order creation")
259259

260260
order_request = CreateOrderRequest(
261261
accountId=self.config.account_id,
@@ -270,7 +270,7 @@ async def create_limit_order(self, params: LimitOrderParameters) -> CreateOrderR
270270
reduceOnly=params.reduce_only,
271271
signature=signature,
272272
nonce=str(nonce),
273-
signerWallet=self.config.wallet_address,
273+
signerWallet=self.signer_wallet_address,
274274
)
275275

276276
response = await self.orders.create_order(create_order_request=order_request)
@@ -329,8 +329,6 @@ async def create_trigger_order(self, params: TriggerOrderParameters) -> CreateOr
329329

330330
if self.config.account_id is None:
331331
raise ValueError("Account ID is required for order creation")
332-
if self.config.wallet_address is None:
333-
raise ValueError("Wallet address is required for order creation")
334332

335333
order_request = CreateOrderRequest(
336334
accountId=self.config.account_id,
@@ -343,7 +341,7 @@ async def create_trigger_order(self, params: TriggerOrderParameters) -> CreateOr
343341
expiresAfter=None,
344342
signature=signature,
345343
nonce=str(nonce),
346-
signerWallet=self.config.wallet_address,
344+
signerWallet=self.signer_wallet_address,
347345
)
348346

349347
response = await self.orders.create_order(create_order_request=order_request)
@@ -379,111 +377,111 @@ async def get_positions(self, wallet_address: Optional[str] = None) -> list[Posi
379377
Get positions for a wallet address asynchronously.
380378
381379
Args:
382-
wallet_address: Optional wallet address (defaults to current wallet)
380+
wallet_address: Optional wallet address (defaults to owner_wallet_address)
383381
384382
Returns:
385383
Positions data
386384
387385
Raises:
388386
ValueError: If no wallet address is available or API returns an error
389387
"""
390-
wallet = wallet_address or self.wallet_address
388+
wallet = wallet_address or self.owner_wallet_address
391389
if not wallet:
392390
raise ValueError("No wallet address available. Private key must be provided.")
393391

394392
return await self.wallet.get_wallet_positions(address=wallet)
395393

396394
async def get_open_orders(self) -> list[Order]:
397395
"""
398-
Get open orders for the authenticated wallet asynchronously.
396+
Get open orders for the owner wallet asynchronously.
399397
400398
Returns:
401399
List of open orders
402400
403401
Raises:
404402
ValueError: If no wallet address is available or API returns an error
405403
"""
406-
wallet = self.wallet_address
404+
wallet = self.owner_wallet_address
407405
if not wallet:
408406
raise ValueError("No wallet address available. Private key must be provided.")
409407

410408
return await self.wallet.get_wallet_open_orders(address=wallet)
411409

412410
async def get_configuration(self) -> WalletConfiguration:
413411
"""
414-
Get account configuration asynchronously.
412+
Get account configuration for the owner wallet asynchronously.
415413
416414
Returns:
417415
Account configuration information
418416
419417
Raises:
420418
ValueError: If no wallet address is available or API returns an error
421419
"""
422-
wallet = self.wallet_address
420+
wallet = self.owner_wallet_address
423421
if not wallet:
424422
raise ValueError("No wallet address available. Private key must be provided.")
425423

426424
return await self.wallet.get_wallet_configuration(address=wallet)
427425

428426
async def get_perp_executions(self) -> PerpExecutionList:
429427
"""
430-
Get perp executions for the authenticated wallet asynchronously.
428+
Get perp executions for the owner wallet asynchronously.
431429
432430
Returns:
433431
Dictionary containing trades data and metadata
434432
435433
Raises:
436434
ValueError: If no wallet address is available or API returns an error
437435
"""
438-
wallet = self.wallet_address
436+
wallet = self.owner_wallet_address
439437
if not wallet:
440438
raise ValueError("No wallet address available. Private key must be provided.")
441439

442440
return await self.wallet.get_wallet_perp_executions(address=wallet)
443441

444442
async def get_accounts(self) -> list[Account]:
445443
"""
446-
Get accounts for the authenticated wallet asynchronously.
444+
Get accounts for the owner wallet asynchronously.
447445
448446
Returns:
449447
Account information
450448
451449
Raises:
452450
ValueError: If no wallet address is available or API returns an error
453451
"""
454-
wallet = self.wallet_address
452+
wallet = self.owner_wallet_address
455453
if not wallet:
456454
raise ValueError("No wallet address available. Private key must be provided.")
457455

458456
return await self.wallet.get_wallet_accounts(address=wallet)
459457

460458
async def get_account_balances(self) -> list[AccountBalance]:
461459
"""
462-
Get account balances for the authenticated wallet asynchronously.
460+
Get account balances for the owner wallet asynchronously.
463461
464462
Returns:
465463
Account balances
466464
467465
Raises:
468466
ValueError: If no wallet address is available or API returns an error
469467
"""
470-
wallet = self.wallet_address
468+
wallet = self.owner_wallet_address
471469
if not wallet:
472470
raise ValueError("No wallet address available. Private key must be provided.")
473471

474472
return await self.wallet.get_wallet_account_balances(address=wallet)
475473

476474
async def get_spot_executions(self) -> SpotExecutionList:
477475
"""
478-
Get spot executions (i.e. auto exchanges) for the authenticated wallet asynchronously.
476+
Get spot executions (i.e. auto exchanges) for the owner wallet asynchronously.
479477
480478
Returns:
481479
Spot executions
482480
483481
Raises:
484482
ValueError: If no wallet address is available or API returns an error
485483
"""
486-
wallet = self.wallet_address
484+
wallet = self.owner_wallet_address
487485
if not wallet:
488486
raise ValueError("No wallet address available. Private key must be provided.")
489487

0 commit comments

Comments
 (0)