Exemple #1
0
    def test_private_without_stark_private_key(self):
        # Generate STARK keys and Ethhereum account.
        api_private_key = generate_private_key_hex_unsafe()
        stark_private_key = generate_private_key_hex_unsafe()
        eth_account = Web3(None).eth.account.create()

        # Get public keys.
        api_public_key = private_key_to_public_hex(api_private_key)
        stark_public_key = private_key_to_public_hex(stark_private_key)

        # Onboard the user.
        Client(
            host=API_HOST,
            eth_private_key=eth_account.key,
        ).onboarding.create_user(
            api_public_key=api_public_key,
            stark_public_key=stark_public_key,
        )

        # Create a second client WITHOUT eth_private_key or stark_private_key.
        client = Client(
            host=API_HOST,
            api_private_key=api_private_key,
        )

        # Create a test deposit.
        client.private.create_test_deposit(
            from_address=eth_account.address,
            credit_amount='200',
        )

        # Get the primary account.
        get_account_result = client.private.get_account(
            ethereum_address=eth_account.address, )
        account = get_account_result['account']

        # Sign a withdrawal.
        client_id = random_client_id()
        expiration = epoch_seconds_to_iso(time.time() + 60)
        signable_withdrawal = SignableWithdrawal(
            position_id=account['positionId'],
            client_id=client_id,
            human_amount='1',
            expiration_epoch_seconds=iso_to_epoch_seconds(expiration),
        )
        signature = signable_withdrawal.sign(stark_private_key)

        # Initiate a withdrawal.
        client.private.create_withdrawal(
            position_id=account['positionId'],
            amount='1',
            asset=constants.ASSET_USDC,
            to_address=eth_account.address,
            expiration=expiration,
            client_id=client_id,
            signature=signature,
        )
Exemple #2
0
    def test_onboard_with_web3_default_account(self):
        # Generate private key.
        stark_private_key = generate_private_key_hex_unsafe()

        # Get public key.
        stark_public_key, stark_public_key_y_coordinate = (
            private_key_to_public_key_pair_hex(stark_private_key))

        # Connect to local Ethereum node.
        web3 = Web3()
        web3.eth.defaultAccount = web3.eth.accounts[1]

        # Create client WITHOUT any private keys.
        client = Client(
            host=HOST,
            network_id=NETWORK_ID,
            web3=web3,
        )

        # Onboard the user.
        try:
            client.onboarding.create_user(
                stark_public_key=stark_public_key,
                stark_public_key_y_coordinate=stark_public_key_y_coordinate,
            )

        # If the Ethereum address was already onboarded, ignore the error.
        except DydxApiError:
            pass

        # Register and then revoke a second API key.
        client.api_keys.create_api_key()
        client.private.get_api_keys()
        client.api_keys.delete_api_key(
            api_key=client.api_key_credentials['key'], )
Exemple #3
0
    def test_onboard_with_web3_provider(self):
        # Generate private keys.
        api_private_key = generate_private_key_hex_unsafe()
        stark_private_key = generate_private_key_hex_unsafe()

        # Get public keys.
        api_public_key = private_key_to_public_hex(api_private_key)
        stark_public_key = private_key_to_public_hex(stark_private_key)

        # Get account address from local Ethereum node.
        ethereum_address = Web3().eth.accounts[0]

        # Create client WITHOUT any private keys.
        client = Client(
            host=API_HOST,
            web3_provider=Web3.HTTPProvider('http://localhost:8545'),
        )

        # Onboard the user.
        try:
            client.onboarding.create_user(
                ethereum_address=ethereum_address,
                stark_public_key=stark_public_key,
                api_public_key=api_public_key,
            )

        # If the Ethereum address was already onboarded, ignore the error.
        except DydxApiError:
            pass

        # Register and then revoke a second API key.
        api_private_key_2 = generate_private_key_hex_unsafe()
        api_public_key_2 = private_key_to_public_hex(api_private_key_2)
        client.api_keys.register_api_key(
            api_public_key=api_public_key_2,
            ethereum_address=ethereum_address,
        )
        client.api_keys.get_api_keys(ethereum_address=ethereum_address, )
        client.api_keys.delete_api_key(
            api_public_key=api_public_key_2,
            ethereum_address=ethereum_address,
        )
Exemple #4
0
    def test_onboard_with_private_keys(self):
        # Generate keys.
        api_private_key = generate_private_key_hex_unsafe()
        stark_private_key = generate_private_key_hex_unsafe()
        eth_private_key = Web3(None).eth.account.create().key

        # Create client WITH private keys.
        client = Client(
            host=API_HOST,
            api_private_key=api_private_key,
            stark_private_key=stark_private_key,
            eth_private_key=eth_private_key,
        )

        # Onboard the user.
        client.onboarding.create_user()

        # Register and then revoke a second API key.
        api_private_key_2 = generate_private_key_hex_unsafe()
        api_public_key_2 = private_key_to_public_hex(api_private_key_2)
        client.api_keys.register_api_key(api_public_key_2)
        client.api_keys.get_api_keys()
        client.api_keys.delete_api_key(api_public_key_2)
Exemple #5
0
    def test_private_with_private_keys(self):
        # Generate STARK keys and Ethhereum account.
        stark_private_key = generate_private_key_hex_unsafe()
        eth_account = Web3(None).eth.account.create()

        # Get public key.
        stark_public_key, stark_public_key_y_coordinate = (
            private_key_to_public_key_pair_hex(stark_private_key))

        # Onboard the user.
        Client(
            host=HOST,
            network_id=NETWORK_ID,
            eth_private_key=eth_account.key,
        ).onboarding.create_user(
            stark_public_key=stark_public_key,
            stark_public_key_y_coordinate=stark_public_key_y_coordinate,
        )

        # Create a second client WITHOUT eth_private_key.
        client = Client(
            host=HOST,
            network_id=NETWORK_ID,
            stark_private_key=stark_private_key,
        )

        # Get the primary account.
        get_account_result = client.private.get_account(
            ethereum_address=eth_account.address, )
        account = get_account_result['account']

        # Initiate a regular (slow) withdrawal.
        #
        # Expect signature validation to pass, although the collateralization
        # check will fail.
        expected_error = (
            'Withdrawal would put account under collateralization minumum')
        expiration_epoch_seconds = time.time() + SEVEN_DAYS_S + 60
        try:
            client.private.create_withdrawal(
                position_id=account['positionId'],
                amount='1',
                asset=constants.ASSET_USDC,
                to_address=eth_account.address,
                expiration_epoch_seconds=expiration_epoch_seconds,
            )
        except DydxApiError as e:
            if expected_error not in str(e):
                raise
Exemple #6
0
    def test_onboard_with_web3_default_account(self):
        # Generate private keys.
        api_private_key = generate_private_key_hex_unsafe()
        stark_private_key = generate_private_key_hex_unsafe()

        # Get public keys.
        api_public_key = private_key_to_public_hex(api_private_key)
        stark_public_key = private_key_to_public_hex(stark_private_key)

        # Connect to local Ethereum node.
        web3 = Web3()
        web3.eth.defaultAccount = web3.eth.accounts[1]

        # Create client WITHOUT any private keys.
        client = Client(
            host=API_HOST,
            web3=web3,
        )

        # Onboard the user.
        try:
            client.onboarding.create_user(
                stark_public_key=stark_public_key,
                api_public_key=api_public_key,
            )

        # If the Ethereum address was already onboarded, ignore the error.
        except DydxApiError:
            pass

        # Register and then revoke a second API key.
        api_private_key_2 = generate_private_key_hex_unsafe()
        api_public_key_2 = private_key_to_public_hex(api_private_key_2)
        client.api_keys.register_api_key(api_public_key_2)
        client.api_keys.get_api_keys()
        client.api_keys.delete_api_key(api_public_key_2)
Exemple #7
0
    def test_onboard_with_private_keys(self):
        # Generate keys.
        stark_private_key = generate_private_key_hex_unsafe()
        eth_private_key = Web3(None).eth.account.create().key

        # Create client WITH private keys.
        client = Client(
            host=HOST,
            network_id=NETWORK_ID,
            stark_private_key=stark_private_key,
            eth_private_key=eth_private_key,
        )

        # Onboard the user.
        client.onboarding.create_user()

        # Register and then revoke a second API key.
        client.api_keys.create_api_key()
        client.private.get_api_keys()
Exemple #8
0
    def test_onboard_with_web3_provider(self):
        # Generate private key.
        stark_private_key = generate_private_key_hex_unsafe()

        # Get public key.
        stark_public_key, stark_public_key_y_coordinate = (
            private_key_to_public_key_pair_hex(stark_private_key))

        # Get account address from local Ethereum node.
        ethereum_address = Web3().eth.accounts[0]

        # Create client WITHOUT any private keys.
        client = Client(
            host=HOST,
            network_id=NETWORK_ID,
            web3_provider=Web3.HTTPProvider('http://localhost:8545'),
        )

        # Onboard the user.
        try:
            client.onboarding.create_user(
                ethereum_address=ethereum_address,
                stark_public_key=stark_public_key,
                stark_public_key_y_coordinate=stark_public_key_y_coordinate,
            )

        # If the Ethereum address was already onboarded, ignore the error.
        except DydxApiError:
            pass

        # Register and then revoke a second API key.
        client.api_keys.create_api_key(ethereum_address=ethereum_address, )
        client.private.get_api_keys()
        client.api_keys.delete_api_key(
            api_key=client.api_key_credentials['key'],
            ethereum_address=ethereum_address,
        )
Exemple #9
0
    def test_integration_without_funds(self):
        # Create an Ethereum account and STARK keys for the new user.
        web3_account = Web3(None).eth.account.create()
        ethereum_address = web3_account.address
        stark_private_key = generate_private_key_hex_unsafe()

        # Create client for the new user.
        client = Client(
            host=HOST,
            network_id=NETWORK_ID,
            stark_private_key=stark_private_key,
            web3_account=web3_account,
        )

        # Onboard the user.
        client.onboarding.create_user()

        # Register a new API key.
        client.api_keys.create_api_key()

        # Get the primary account.
        get_account_result = client.private.get_account(
            ethereum_address=ethereum_address, )
        account = get_account_result['account']
        assert int(account['starkKey'], 16) == int(client.stark_public_key, 16)

        # Initiate a regular (slow) withdrawal.
        #
        # Expect signature validation to pass, although the collateralization
        # check will fail.
        expected_error = (
            'Withdrawal would put account under collateralization minumum')
        expiration_epoch_seconds = time.time() + SEVEN_DAYS_S + 60
        try:
            client.private.create_withdrawal(
                position_id=account['positionId'],
                amount='1',
                asset=constants.ASSET_USDC,
                to_address=ethereum_address,
                expiration_epoch_seconds=expiration_epoch_seconds,
            )
        except DydxApiError as e:
            if expected_error not in str(e):
                raise

        # Post an order.
        #
        # Expect signature validation to pass, although the collateralization
        # check will fail.
        one_minute_from_now_iso = epoch_seconds_to_iso(time.time() + 60)
        try:
            client.private.create_order(
                position_id=account['positionId'],
                market=constants.MARKET_BTC_USD,
                side=constants.ORDER_SIDE_BUY,
                order_type=constants.ORDER_TYPE_LIMIT,
                post_only=False,
                size='10',
                price='1000',
                limit_fee='0.1',
                expiration=one_minute_from_now_iso,
            )
        except DydxApiError as e:
            if expected_error not in str(e):
                raise
Exemple #10
0
    def test_integration(self):
        source_private_key = os.environ.get('TEST_SOURCE_PRIVATE_KEY')
        if source_private_key is None:
            raise ValueError('TEST_SOURCE_PRIVATE_KEY must be set')

        web3_provider = os.environ.get('TEST_WEB3_PROVIDER_URL')
        if web3_provider is None:
            raise ValueError('TEST_WEB3_PROVIDER_URL must be set')

        # Create client that will be used to fund the new user.
        source_client = Client(
            host='',
            eth_private_key=source_private_key,
            web3_provider=web3_provider,
        )

        # Create an Ethereum account and STARK keys for the new user.
        web3_account = Web3(None).eth.account.create()
        ethereum_address = web3_account.address
        eth_private_key = web3_account.key
        stark_private_key = generate_private_key_hex_unsafe()

        # Fund the new user with ETH and USDC.
        fund_eth_hash = source_client.eth.transfer_eth(
            to_address=ethereum_address,
            human_amount=0.001,
        )
        fund_usdc_hash = source_client.eth.transfer_token(
            to_address=ethereum_address,
            human_amount=2,
        )
        print('Waiting for funds...')
        source_client.eth.wait_for_tx(fund_eth_hash)
        source_client.eth.wait_for_tx(fund_usdc_hash)
        print('...done.')

        # Create client for the new user.
        client = Client(
            host=HOST,
            network_id=NETWORK_ID,
            stark_private_key=stark_private_key,
            eth_private_key=eth_private_key,
            web3_provider=web3_provider,
        )

        # Onboard the user.
        res = client.onboarding.create_user()
        api_key_credentials = res['apiKey']

        print('eth_private_key', eth_private_key)
        print('stark_private_key', stark_private_key)
        print('client.api_key_credentials', client.api_key_credentials)

        # Get the user.
        get_user_result = client.private.get_user()
        assert get_user_result['user'] == {
            'ethereumAddress': ethereum_address.lower(),
            'isRegistered': False,
            'email': None,
            'username': None,
            'userData': {},
            'makerFeeRate': '0.0005',
            'takerFeeRate': '0.0015',
            'makerVolume30D': '0',
            'takerVolume30D': '0',
            'fees30D': '0',
        }

        # Get the registration signature.
        registration_result = client.private.get_registration()
        signature = registration_result['signature']
        assert re.match('0x[0-9a-f]{130}$', signature) is not None, (
            'Invalid registration result: {}'.format(registration_result))

        # Register the user on-chain.
        registration_tx_hash = client.eth.register_user(signature)
        print('Waiting for registration...')
        client.eth.wait_for_tx(registration_tx_hash)
        print('...done.')

        # Set the user's username.
        username = '******'.format(int(time.time()))
        client.private.update_user(username=username)

        # Create a second account under the same user.
        #
        # NOTE: Support for multiple accounts under the same user is limited.
        # The frontend does not currently support mutiple accounts per user.
        stark_private_key_2 = generate_private_key_hex_unsafe()
        stark_public_key_2, stark_public_key_y_coordinate_2 = (
            private_key_to_public_key_pair_hex(stark_private_key_2))

        client.private.create_account(
            stark_public_key=stark_public_key_2,
            stark_public_key_y_coordinate=stark_public_key_y_coordinate_2,
        )

        # Get the primary account.
        get_account_result = client.private.get_account(
            ethereum_address=ethereum_address, )
        account = get_account_result['account']
        assert int(account['starkKey'], 16) == int(client.stark_public_key, 16)

        # Get all accounts.
        get_all_accounts_result = client.private.get_accounts()
        get_all_accounts_public_keys = [
            a['starkKey'] for a in get_all_accounts_result['accounts']
        ]
        assert int(client.stark_public_key,
                   16) in [int(k, 16) for k in get_all_accounts_public_keys]

        # TODO: Fix.
        # assert int(stark_public_key_2, 16) in [
        #     int(k, 16) for k in get_all_accounts_public_keys
        # ]

        # Get positions.
        get_positions_result = client.private.get_positions(market='BTC-USD')
        assert get_positions_result == {'positions': []}

        # Set allowance on the Starkware perpetual contract, for the deposit.
        approve_tx_hash = client.eth.set_token_max_allowance(
            client.eth.get_exchange_contract().address, )
        print('Waiting for allowance...')
        client.eth.wait_for_tx(approve_tx_hash)
        print('...done.')

        # Send an on-chain deposit.
        deposit_tx_hash = client.eth.deposit_to_exchange(
            account['positionId'],
            3,
        )
        print('Waiting for deposit...')
        client.eth.wait_for_tx(deposit_tx_hash)
        print('...done.')

        # Wait for the deposit to be processed.
        print('Waiting for deposit to be processed on dYdX...')
        wait_for_condition(
            lambda: len(client.private.get_transfers()['transfers']) > 0,
            True,
            60,
        )
        print('...transfer was recorded, waiting for confirmation...')
        wait_for_condition(
            lambda: client.private.get_account()['account']['quoteBalance'],
            '2',
            180,
        )
        print('...done.')

        # Post an order.
        one_minute_from_now_iso = epoch_seconds_to_iso(time.time() + 60)
        create_order_result = client.private.create_order(
            position_id=account['positionId'],
            market=constants.MARKET_BTC_USD,
            side=constants.ORDER_SIDE_BUY,
            order_type=constants.ORDER_TYPE_LIMIT,
            post_only=False,
            size='10',
            price='1000',
            limit_fee='0.1',
            expiration=one_minute_from_now_iso,
        )

        # Get the order.
        order_id = create_order_result['order']['id']
        get_order_result = client.private.get_order_by_id(order_id)
        assert get_order_result['order']['market'] == constants.MARKET_BTC_USD

        # Cancel the order.
        client.private.cancel_order(order_id)

        # Cancel all orders.
        client.private.cancel_all_orders()

        # Get open orders.
        get_orders_result = client.private.get_orders(
            market=constants.MARKET_BTC_USD,
            status=constants.POSITION_STATUS_OPEN,
        )
        assert get_orders_result == {'orders': []}

        # Get fills.
        client.private.get_fills(market=constants.MARKET_BTC_USD, )

        # Initiate a regular (slow) withdrawal.
        expiration_epoch_seconds = time.time() + SEVEN_DAYS_S + 60
        client.private.create_withdrawal(
            position_id=account['positionId'],
            amount='1',
            asset=constants.ASSET_USDC,
            to_address=ethereum_address,
            expiration_epoch_seconds=expiration_epoch_seconds,
        )

        # Get deposits.
        deposits_result = client.private.get_transfers(
            transfer_type=constants.ACCOUNT_ACTION_DEPOSIT, )
        assert len(deposits_result['transfers']) == 1

        # Get withdrawals.
        withdrawals_result = client.private.get_transfers(
            transfer_type=constants.ACCOUNT_ACTION_WITHDRAWAL, )
        assert len(withdrawals_result['transfers']) == 1

        # Get funding payments.
        client.private.get_funding_payments(market=constants.MARKET_BTC_USD, )

        # Register a new API key.
        create_api_key_result = client.api_keys.create_api_key()
        new_api_key_credentials = create_api_key_result['apiKey']

        # Get all API keys.
        get_api_keys_result = client.private.get_api_keys()
        api_keys_public_keys = [
            a['key'] for a in get_api_keys_result['apiKeys']
        ]
        assert api_key_credentials['key'] in api_keys_public_keys

        # Delete an API key.
        client.api_keys.delete_api_key(
            api_key=new_api_key_credentials['key'],
            ethereum_address=ethereum_address,
        )

        # Get all API keys after the deletion.
        get_api_keys_result_after = client.private.get_api_keys()
        assert len(get_api_keys_result_after['apiKeys']) == 1

        # Initiate a fast withdrawal of USDC.
        expiration_epoch_seconds = time.time() + SEVEN_DAYS_S + 60
        client.private.create_fast_withdrawal(
            position_id=account['positionId'],
            credit_asset='USDC',
            credit_amount='1',
            debit_amount='2',
            to_address=ethereum_address,
            lp_position_id=LP_POSITION_ID,
            lp_stark_public_key=LP_PUBLIC_KEY,
            client_id='mock-client-id',
            expiration_epoch_seconds=expiration_epoch_seconds,
        )
Exemple #11
0
    def test_integration(self):
        # Create an Ethereum account.
        web3 = Web3(None)
        web3_account = web3.eth.account.create()
        ethereum_address = web3_account.address

        # Generate STARK keys.
        api_private_key = generate_private_key_hex_unsafe()
        stark_private_key = generate_private_key_hex_unsafe()

        # Create client.
        client = Client(
            host=os.environ.get('V3_API_HOST', DEFAULT_HOST),
            api_private_key=api_private_key,
            stark_private_key=stark_private_key,
            web3_account=web3_account,
        )

        # Onboard user.
        client.onboarding.create_user(ethereum_address=ethereum_address, )

        # Get the user.
        get_user_result = client.private.get_user()
        assert get_user_result == {
            'user': {
                'ethereumAddress': ethereum_address.lower(),
                'isRegistered': False,
                'email': None,
                'username': None,
                'userData': {},
                'makerFeeRate': '0.05',
                'takerFeeRate': '0.04',
                'makerVolume30D': '0',
                'takerVolume30D': '0',
                'fees30D': '0',
            },
        }

        # Get the registration signature.
        get_registration_result = client.private.get_registration()
        assert re.match(
            '0x[0-9a-f]{130}$',
            get_registration_result['signature'],
        ) is not None, (
            'Invalid registration result: {}'.format(get_registration_result))

        # Set the user's username.
        username = '******'.format(int(time.time()))
        client.private.update_user(username=username)

        # Create a second account under the same user.
        #
        # NOTE: Support for multiple accounts under the same user is limited.
        # The frontend does not currently support mutiple accounts per user.
        stark_private_key_2 = generate_private_key_hex_unsafe()
        stark_public_key_2 = private_key_to_public_hex(stark_private_key_2)
        client.private.create_account(stark_public_key=stark_public_key_2, )

        # Get the primary account.
        get_account_result = client.private.get_account(
            ethereum_address=ethereum_address, )
        account = get_account_result['account']
        assert account['starkKey'] == client.stark_public_key

        # Get all accounts.
        get_all_accounts_result = client.private.get_accounts()
        get_all_accounts_public_keys = [
            a['starkKey'] for a in get_all_accounts_result['accounts']
        ]
        assert client.stark_public_key in get_all_accounts_public_keys
        assert stark_public_key_2 in get_all_accounts_public_keys

        # Get postiions.
        get_positions_result = client.private.get_positions(market='BTC-USD')
        assert get_positions_result == {'positions': []}

        # Create a test deposit.
        client.private.create_test_deposit(
            from_address=ethereum_address,
            credit_amount='200',
        )

        # Post an order.
        one_minute_from_now_iso = epoch_seconds_to_iso(time.time() + 60)
        create_order_result = client.private.create_order(
            position_id=account['positionId'],
            market=constants.MARKET_BTC_USD,
            side=constants.ORDER_SIDE_BUY,
            order_type=constants.ORDER_TYPE_LIMIT,
            post_only=False,
            size='10',
            price='1000',
            limit_fee='0.1',
            expiration=one_minute_from_now_iso,
        )

        # Get the order.
        order_id = create_order_result['order']['id']
        get_order_result = client.private.get_order_by_id(order_id)
        assert get_order_result['order']['market'] == constants.MARKET_BTC_USD

        # Cancel the order.
        client.private.cancel_order(order_id)

        # Cancel all orders.
        client.private.cancel_all_orders()

        # Get open orders.
        get_orders_result = client.private.get_orders(
            market=constants.MARKET_BTC_USD,
            status=constants.POSITION_STATUS_OPEN,
        )
        assert get_orders_result == {'orders': []}

        # Get fills.
        client.private.get_fills(market=constants.MARKET_BTC_USD, )

        # Initiate a regular (slow) withdrawal.
        one_minute_from_now_iso = epoch_seconds_to_iso(time.time() + 60)
        client.private.create_withdrawal(
            position_id=account['positionId'],
            amount='1',
            asset=constants.ASSET_USDC,
            to_address=ethereum_address,
            expiration=one_minute_from_now_iso,
        )

        # Get deposits.
        deposits_result = client.private.get_transfers(
            transfer_type=constants.ACCOUNT_ACTION_DEPOSIT, )
        assert len(deposits_result['transfers']) == 1

        # Get withdrawals.
        withdrawals_result = client.private.get_transfers(
            transfer_type=constants.ACCOUNT_ACTION_WITHDRAWAL, )
        assert len(withdrawals_result['transfers']) == 1

        # Get funding payments.
        client.private.get_funding_payments(market=constants.MARKET_BTC_USD, )

        # Register a new API key.
        api_private_key_2 = generate_private_key_hex_unsafe()
        api_public_key_2 = private_key_to_public_hex(api_private_key_2)
        client.api_keys.register_api_key(
            ethereum_address=ethereum_address,
            api_public_key=api_public_key_2,
        )

        # Get all API keys.
        api_keys_result = client.api_keys.get_api_keys(
            ethereum_address=ethereum_address, )
        api_keys_public_keys = [
            a['apiKey'] for a in api_keys_result['apiKeys']
        ]
        assert client.api_public_key in api_keys_public_keys
        assert api_public_key_2 in api_keys_public_keys

        # Delete an API key.
        client.api_keys.delete_api_key(
            ethereum_address=ethereum_address,
            api_public_key=api_public_key_2,
        )

        # Get all API keys after the deletion.
        api_keys_result_after = client.api_keys.get_api_keys(
            ethereum_address=ethereum_address, )
        assert len(api_keys_result_after['apiKeys']) == 1