def test_nonce_from_client_id(self):
     assert nonce_from_client_id('') == 2018687061
     assert nonce_from_client_id('1') == 3079101259
     assert nonce_from_client_id('a') == 2951628987
     assert nonce_from_client_id(
         'A really long client ID used to identify an order or withdrawal',
     ) == 2913863714
     assert nonce_from_client_id(
         'A really long client ID used to identify an order or withdrawal!',
     ) == 230317226
 def __init__(
     self,
     network_id,
     sender_position_id,
     receiver_position_id,
     receiver_public_key,
     fact_registry_address,
     fact,
     human_amount,
     client_id,
     expiration_epoch_seconds,
 ):
     receiver_public_key = (receiver_public_key if isinstance(
         receiver_public_key, int) else int(receiver_public_key, 16))
     quantums_amount = to_quantums_exact(human_amount, COLLATERAL_ASSET)
     expiration_epoch_hours = math.ceil(
         float(expiration_epoch_seconds) / ONE_HOUR_IN_SECONDS, )
     message = StarkwareConditionalTransfer(
         sender_position_id=int(sender_position_id),
         receiver_position_id=int(receiver_position_id),
         receiver_public_key=receiver_public_key,
         condition=fact_to_condition(fact_registry_address, fact),
         quantums_amount=quantums_amount,
         nonce=nonce_from_client_id(client_id),
         expiration_epoch_hours=expiration_epoch_hours,
     )
     super(SignableConditionalTransfer, self).__init__(
         network_id,
         message,
     )
Beispiel #3
0
    def __init__(
        self,
        market,
        side,
        position_id,
        human_size,
        human_price,
        human_limit_fee,  # TODO: Is this an amount or a percentage?
        client_id,
        expiration_epoch_seconds,
    ):
        is_buying_synthetic = side == ORDER_SIDE_BUY
        synthetic_asset = SYNTHETIC_ASSET_MAP[market]
        quantums_amount_synthetic = to_quantums_exact(
            human_size,
            synthetic_asset,
        )
        quantums_amount_fee = to_quantums_exact(
            human_limit_fee,
            COLLATERAL_ASSET,
        )

        # Note: By creating the decimals outside the context and then
        # multiplying within the context, we ensure rounding does not occur
        # until after the multiplication is computed with full precision.
        if is_buying_synthetic:
            human_cost = DECIMAL_CONTEXT_ROUND_UP.multiply(
                decimal.Decimal(human_size), decimal.Decimal(human_price))
            quantums_amount_collateral = to_quantums_round_up(
                human_cost,
                COLLATERAL_ASSET,
            )
        else:
            human_cost = DECIMAL_CONTEXT_ROUND_DOWN.multiply(
                decimal.Decimal(human_size), decimal.Decimal(human_price))
            quantums_amount_collateral = to_quantums_round_down(
                human_cost,
                COLLATERAL_ASSET,
            )

        message = StarkwareOrder(
            order_type='LIMIT_ORDER_WITH_FEES',
            asset_id_synthetic=ASSET_ID_MAP[synthetic_asset],
            asset_id_collateral=COLLATERAL_ASSET_ID,
            asset_id_fee=COLLATERAL_ASSET_ID,
            quantums_amount_synthetic=quantums_amount_synthetic,
            quantums_amount_collateral=quantums_amount_collateral,
            quantums_amount_fee=quantums_amount_fee,
            is_buying_synthetic=is_buying_synthetic,
            position_id=int(position_id),
            nonce=nonce_from_client_id(client_id),
            expiration_epoch_seconds=expiration_epoch_seconds,
        )
        super(SignableOrder, self).__init__(message)
 def __init__(
     self,
     position_id,
     human_amount,
     client_id,
     expiration_epoch_seconds,
 ):
     quantums_amount = to_quantums_exact(human_amount, COLLATERAL_ASSET)
     message = StarkwareWithdrawal(
         quantums_amount=quantums_amount,
         position_id=int(position_id),
         nonce=nonce_from_client_id(client_id),
         expiration_epoch_seconds=expiration_epoch_seconds,
     )
     super(SignableWithdrawal, self).__init__(message)
Beispiel #5
0
 def __init__(
     self,
     network_id,
     position_id,
     human_amount,
     client_id,
     expiration_epoch_seconds,
 ):
     quantums_amount = to_quantums_exact(human_amount, COLLATERAL_ASSET)
     expiration_epoch_hours = math.ceil(
         float(expiration_epoch_seconds) / ONE_HOUR_IN_SECONDS, )
     message = StarkwareWithdrawal(
         quantums_amount=quantums_amount,
         position_id=int(position_id),
         nonce=nonce_from_client_id(client_id),
         expiration_epoch_hours=expiration_epoch_hours,
     )
     super(SignableWithdrawal, self).__init__(network_id, message)
Beispiel #6
0
    def create_fast_withdrawal(
        self,
        position_id,
        credit_asset,
        credit_amount,
        debit_amount,
        to_address,
        lp_position_id,
        lp_stark_public_key,
        client_id=None,
        expiration=None,
        expiration_epoch_seconds=None,
        signature=None,
    ):
        '''
        Post a fast withdrawal

        :param credit_asset: required
        :type credit_asset: str in list [
            "USDC",
            "USDT",
        ]

        :param position_id: required
        :type position_id: str or int

        :param credit_amount: required
        :type credit_amount: str or int

        :param debit_amount: required
        :type debit_amount: str or int

        :param to_address: required
        :type to_address: str

        :param lp_position_id: required
        :type lp_position_id: str or int

        :param lp_stark_public_key: required
        :type lp_stark_public_key: str

        :param client_id: optional
        :type client_id: str

        :param expiration: optional
        :type expiration: ISO str

        :param expiration_epoch_seconds: optional
        :type expiration_epoch_seconds: int

        :param signature: optional
        :type signature: str

        :returns: Transfer

        :raises: DydxAPIError
        '''
        client_id = client_id or random_client_id()
        if bool(expiration) == bool(expiration_epoch_seconds):
            raise ValueError(
                'Exactly one of expiration and expiration_epoch_seconds must '
                'be specified', )
        expiration = expiration or epoch_seconds_to_iso(
            expiration_epoch_seconds, )
        expiration_epoch_seconds = (expiration_epoch_seconds
                                    or iso_to_epoch_seconds(expiration))

        if not signature:
            if not self.stark_private_key:
                raise Exception('No signature provided and client was not' +
                                'initialized with stark_private_key')
            fact = get_transfer_erc20_fact(
                recipient=to_address,
                token_decimals=COLLATERAL_TOKEN_DECIMALS,
                human_amount=credit_amount,
                token_address=(
                    TOKEN_CONTRACTS[COLLATERAL_ASSET][self.network_id]),
                salt=nonce_from_client_id(client_id),
            )
            transfer_to_sign = SignableConditionalTransfer(
                network_id=self.network_id,
                sender_position_id=position_id,
                receiver_position_id=lp_position_id,
                receiver_public_key=lp_stark_public_key,
                fact_registry_address=FACT_REGISTRY_CONTRACT[self.network_id],
                fact=fact,
                human_amount=debit_amount,
                client_id=client_id,
                expiration_epoch_seconds=expiration_epoch_seconds,
            )
            signature = transfer_to_sign.sign(self.stark_private_key)

        params = {
            'creditAsset': credit_asset,
            'creditAmount': credit_amount,
            'debitAmount': debit_amount,
            # TODO: Signature verification should work regardless of case.
            'toAddress': to_address.lower(),
            'lpPositionId': lp_position_id,
            'expiration': expiration,
            'clientId': client_id,
            'signature': signature,
        }
        return self._post('fast-withdrawals', params)
Beispiel #7
0
    def __init__(
        self,
        network_id,
        market,
        side,
        position_id,
        human_size,
        human_price,
        limit_fee,
        client_id,
        expiration_epoch_seconds,
    ):
        synthetic_asset = SYNTHETIC_ASSET_MAP[market]
        synthetic_asset_id = SYNTHETIC_ASSET_ID_MAP[synthetic_asset]
        collateral_asset_id = COLLATERAL_ASSET_ID_BY_NETWORK_ID[network_id]
        is_buying_synthetic = side == ORDER_SIDE_BUY
        quantums_amount_synthetic = to_quantums_exact(
            human_size,
            synthetic_asset,
        )

        # Note: By creating the decimals outside the context and then
        # multiplying within the context, we ensure rounding does not occur
        # until after the multiplication is computed with full precision.
        if is_buying_synthetic:
            human_cost = DECIMAL_CONTEXT_ROUND_UP.multiply(
                decimal.Decimal(human_size),
                decimal.Decimal(human_price)
            )
            quantums_amount_collateral = to_quantums_round_up(
                human_cost,
                COLLATERAL_ASSET,
            )
        else:
            human_cost = DECIMAL_CONTEXT_ROUND_DOWN.multiply(
                decimal.Decimal(human_size),
                decimal.Decimal(human_price)
            )
            quantums_amount_collateral = to_quantums_round_down(
                human_cost,
                COLLATERAL_ASSET,
            )

        # The limitFee is a fraction, e.g. 0.01 is a 1 % fee.
        # It is always paid in the collateral asset.
        # Constrain the limit fee to six decimals of precision.
        # The final fee amount must be rounded up.
        limit_fee_rounded = DECIMAL_CONTEXT_ROUND_DOWN.quantize(
            decimal.Decimal(limit_fee),
            decimal.Decimal('0.000001'),
        )
        quantums_amount_fee_decimal = DECIMAL_CONTEXT_ROUND_UP.multiply(
            limit_fee_rounded,
            quantums_amount_collateral,
        ).to_integral_value(context=DECIMAL_CONTEXT_ROUND_UP)

        # Orders may have a short time-to-live on the orderbook, but we need
        # to ensure their signatures are valid by the time they reach the
        # blockchain. Therefore, we enforce that the signed expiration includes
        # a buffer relative to the expiration timestamp sent to the dYdX API.
        expiration_epoch_hours = math.ceil(
            float(expiration_epoch_seconds) / ONE_HOUR_IN_SECONDS,
        ) + ORDER_SIGNATURE_EXPIRATION_BUFFER_HOURS

        message = StarkwareOrder(
            order_type='LIMIT_ORDER_WITH_FEES',
            asset_id_synthetic=synthetic_asset_id,
            asset_id_collateral=collateral_asset_id,
            asset_id_fee=collateral_asset_id,
            quantums_amount_synthetic=quantums_amount_synthetic,
            quantums_amount_collateral=quantums_amount_collateral,
            quantums_amount_fee=int(quantums_amount_fee_decimal),
            is_buying_synthetic=is_buying_synthetic,
            position_id=int(position_id),
            nonce=nonce_from_client_id(client_id),
            expiration_epoch_hours=expiration_epoch_hours,
        )
        super(SignableOrder, self).__init__(network_id, message)