Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions tests/perpetual/test_withdrawal_object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import datetime

from hamcrest import assert_that, equal_to
from eth_account import Account
from decimal import Decimal

from hamcrest import equal_to

from x10.perpetual.user_client.onboarding import get_l2_keys_from_l1_account
from x10.perpetual.withdrawals import Withdrawal


def test_withdrawal_object_generation():
known_private_key = "50c8e358cc974aaaa6e460641e53f78bdc550fd372984aa78ef8fd27c751e6f4"

l1_account = Account.from_key(known_private_key)

payload = Withdrawal(
account_id=12,
target_wallet="0x1234",
amount=Decimal("1"),
expiration=datetime.datetime.fromtimestamp(1710176400,tz=datetime.timezone.utc),
asset_id="0x1",
)
result = l1_account.sign_message(payload.to_signable_message("x10.exchange")).signature.hex()
assert_that(
result,
equal_to('f1d965cc6c3d020c103e2bd295a0416ab50b0f6c67b01312bf09ea883788df2027a7dad3666c7cca618063b9eda37854642c02dc7842679ccc07e4ea0d0ec0ec1c')
)
2 changes: 2 additions & 0 deletions x10/perpetual/transfer_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def create_transfer_object(
config: EndpointConfig,
stark_account: StarkPerpetualAccount,
nonce: int | None = None,
signature: str | None = None,
) -> OnChainPerpetualTransferModel:
expiration_timestamp = calc_expiration_timestamp()
scaled_amount = amount.scaleb(config.collateral_decimals)
Expand Down Expand Up @@ -77,4 +78,5 @@ def create_transfer_object(
amount=amount,
settlement=settlement,
transferred_asset=config.collateral_asset_id,
signature=signature
)
48 changes: 48 additions & 0 deletions x10/perpetual/transfers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from dataclasses import dataclass
from decimal import Decimal

from x10.perpetual.orders import SettlementSignatureModel
from x10.utils.model import HexValue, X10BaseModel
from datetime import datetime, timezone
from eth_account.messages import SignableMessage, encode_typed_data



class StarkTransferSettlement(X10BaseModel):
Expand Down Expand Up @@ -30,10 +34,54 @@ class OnChainPerpetualTransferModel(X10BaseModel):
amount: Decimal
settlement: StarkTransferSettlement
transferred_asset: str
signature: str


class TransferResponseModel(X10BaseModel):
valid_signature: bool
id: int | None = None
hash_calculated: str | None = None
stark_ex_representation: dict | None = None

@dataclass
class Transfer:
source_account: int
target_account: int
asset_id: str
amount: Decimal
expiration: datetime

def __post_init__(self):
self.expiration_string = self.expiration.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

def to_signable_message(self, signing_domain) -> SignableMessage:
asset = int(self.asset_id, 16)
domain = {"name": signing_domain}

message = {
"sourceAccount": self.source_account,
"targetAccount": self.target_account,
"assetId": asset,
"amount": str(self.amount),
"expiration": self.expiration_string,
}
types = {
"EIP712Domain": [
{"name": "name", "type": "string"}
],
"Transfer": [
{"name": "sourceAccount", "type": "int64"},
{"name": "targetAccount", "type": "int64"},
{"name": "assetId", "type": "int64"},
{"name": "amount", "type": "string"},
{"name": "expiration", "type": "string"}
]
}
primary_type = "Transfer"
structured_data = {
"types": types,
"domain": domain,
"primaryType": primary_type,
"message": message,
}
return encode_typed_data(full_message=structured_data)
22 changes: 13 additions & 9 deletions x10/perpetual/withdrawal_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ def calc_expiration_timestamp():


def create_withdrawal_object(
amount: Decimal,
recipient_stark_address: str,
stark_account: StarkPerpetualAccount,
config: EndpointConfig,
account_id: int,
chain_id: str,
description: str | None = None,
nonce: int | None = None,
quote_id: str | None = None,
amount: Decimal,
recipient_stark_address: str,
stark_account: StarkPerpetualAccount,
config: EndpointConfig,
account_id: int,
chain_id: str,
description: str | None = None,
nonce: int | None = None,
quote_id: str | None = None,
target_wallet: str | None = None,
signature: str | None = None,
) -> WithdrawalRequest:
expiration_timestamp = calc_expiration_timestamp()
scaled_amount = amount.scaleb(config.collateral_decimals)
Expand Down Expand Up @@ -78,4 +80,6 @@ def create_withdrawal_object(
chain_id=chain_id,
quote_id=quote_id,
asset="USD",
target_wallet=target_wallet,
signature=signature
)
50 changes: 50 additions & 0 deletions x10/perpetual/withdrawals.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from dataclasses import dataclass
from datetime import datetime, timezone
from eth_account.messages import SignableMessage, encode_typed_data

from decimal import Decimal

from x10.utils.model import HexValue, SettlementSignatureModel, X10BaseModel
Expand Down Expand Up @@ -25,3 +29,49 @@ class WithdrawalRequest(X10BaseModel):
chain_id: str
quote_id: str | None = None
asset: str
target_wallet: str | None = None
signature: str | None = None


@dataclass
class Withdrawal:
account_id: int
target_wallet: str
asset_id: str
amount: Decimal
expiration: datetime

def __post_init__(self):
self.expiration_string = self.expiration.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

def to_signable_message(self, signing_domain) -> SignableMessage:
domain = {"name": signing_domain}
asset = int(self.asset_id, 16)
message = {
"account": self.account_id,
"targetWallet": self.target_wallet,
"assetId": asset,
"amount": str(self.amount),
"expiration": self.expiration_string,
}
types = {
"EIP712Domain": [
{"name": "name", "type": "string"}
],
"Withdrawal": [
{"name": "account", "type": "int64"},
{"name": "targetWallet", "type": "string"},
{"name": "assetId", "type": "int64"},
{"name": "amount", "type": "string"},
{"name": "expiration", "type": "string"}
]
}
primary_type = "Withdrawal"
structured_data = {
"types": types,
"domain": domain,
"primaryType": primary_type,
"message": message,
}
return encode_typed_data(full_message=structured_data)

Loading