Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option for specifying apikey/secret through opts #107

Merged
merged 7 commits into from
Sep 2, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[
{
"request": {
"body": "",
"headers": {
"X-MBX-APIKEY": "dummy_api_key"
},
"method": "get",
"options": [],
"request_body": "",
"url": "https://api.binance.com/api/v3/openOrders?timestamp=1698985036203&signature=***"
},
"response": {
"binary": false,
"body": "{\"code\":-2014,\"msg\":\"API-key format invalid.\"}",
"headers": {
"Content-Type": "application/json;charset=UTF-8",
"Content-Length": "46",
"Connection": "keep-alive",
"Date": "Fri, 03 Nov 2023 04:17:16 GMT",
"Server": "nginx",
"x-mbx-uuid": "b2c45e59-9b6d-470a-9c78-962dbfa17d9d",
"x-mbx-used-weight": "80",
"x-mbx-used-weight-1m": "80",
"Strict-Transport-Security": "max-age=31536000; includeSubdomains",
"X-Frame-Options": "SAMEORIGIN",
"X-Xss-Protection": "1; mode=block",
"X-Content-Type-Options": "nosniff",
"Content-Security-Policy": "default-src 'self'",
"X-Content-Security-Policy": "default-src 'self'",
"X-WebKit-CSP": "default-src 'self'",
"Cache-Control": "no-cache, no-store, must-revalidate",
"Pragma": "no-cache",
"Expires": "0",
"X-Cache": "Error from cloudfront",
"Via": "1.1 12632bbc89afe55228d7f1ab9e5993a6.cloudfront.net (CloudFront)",
"X-Amz-Cf-Pop": "NRT57-C3",
"X-Amz-Cf-Id": "vAUtPe6jUC-CzKpGocdbeFjnnILu7o6w9tNsBybIiHQwNO4xvETT_g=="
},
"status_code": 401,
"type": "ok"
}
}
]
21 changes: 19 additions & 2 deletions lib/binance.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ docs
|> Enum.map(fn param ->
%{name: param.name, description: param.description}
end)
|> Kernel.++([
%{name: "api_key", description: "Binance API key, will overwrite env key"},
%{name: "secret_key", description: "Binance API secret, will overwrite env secret"}
])

optional_params =
case needs_timestamp do
Expand Down Expand Up @@ -154,7 +158,14 @@ docs
binding = binding()

# merge all passed args together, so opts + passed
all_passed_args = Keyword.merge(binding, opts) |> Keyword.drop([:opts])
all_passed_args =
Keyword.merge(binding, opts) |> Keyword.drop([:opts, :api_key, :secret_key])

api_key =
Keyword.get(opts, :api_key) || Application.get_env(:binance, :api_key, "")

secret_key =
Keyword.get(opts, :secret_key) || Application.get_env(:binance, :secret_key, "")

# if the call requires a timestamp, we add it
adjusted_args =
Expand Down Expand Up @@ -185,7 +196,13 @@ docs

case unquote(needs_auth) do
true ->
case HTTPClient.signed_request_binance(unquote(url), adjusted_args, unquote(method)) do
case HTTPClient.signed_request_binance(
unquote(url),
api_key,
secret_key,
adjusted_args,
unquote(method)
) do
{:ok, %{"code" => _code, "msg" => _msg} = err} ->
{:error, Binance.Helper.format_error(err)}

Expand Down
39 changes: 27 additions & 12 deletions lib/binance/rest/http_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Application.get_env(:binance, :end_point, "https://api.binance.com")
end

defp prepare_request(url, params, secret_key, api_key) do

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (25.3, 1.14.5)

function prepare_request/4 is unused

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / testolder (23.3, 1.14)

function prepare_request/4 is unused

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (26.1, 1.14.5)

function prepare_request/4 is unused

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (25.3, 1.14.5)

function prepare_request/4 is unused

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (24.3, 1.14.5)

function prepare_request/4 is unused

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (26.1, 1.14.5)

function prepare_request/4 is unused

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (24.3, 1.14.5)

function prepare_request/4 is unused

Check warning on line 6 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / testolder (23.3, 1.14)

function prepare_request/4 is unused
case validate_credentials(secret_key, api_key) do
{:error, _} = error ->
error
Expand Down Expand Up @@ -33,7 +33,7 @@
end
end

defp request_binance(url, body, method) do
defp request_binance(url, api_key, body, method) do
url = URI.parse("#{endpoint()}#{url}")

encoded_url =
Expand All @@ -48,15 +48,15 @@
HTTPoison.get(
URI.to_string(encoded_url),
[
{"X-MBX-APIKEY", Application.get_env(:binance, :api_key)}
{"X-MBX-APIKEY", api_key}
]
)

:delete ->
HTTPoison.delete(
URI.to_string(encoded_url),
[
{"X-MBX-APIKEY", Application.get_env(:binance, :api_key)}
{"X-MBX-APIKEY", api_key}
]
)

Expand All @@ -65,7 +65,7 @@
URI.to_string(encoded_url),
"",
[
{"X-MBX-APIKEY", Application.get_env(:binance, :api_key)}
{"X-MBX-APIKEY", api_key}
]
])
end
Expand All @@ -81,23 +81,29 @@
end
end

def signed_request_binance(url, params, method) do
argument_string =
params
|> prepare_query_params()
def signed_request_binance(url, api_key, secret_key, params, method) do
case validate_credentials(secret_key, api_key) do
:ok ->
argument_string =
params
|> prepare_query_params()

# generate signature
signature =
generate_signature(
:sha256,
Application.get_env(:binance, :secret_key),
secret_key,
argument_string
)
|> Base.encode16()

body = "#{argument_string}&signature=#{signature}"
body = "#{argument_string}&signature=#{signature}"

request_binance(url, body, method)
request_binance(url, api_key, body, method)

e ->
e
end
end

@doc """
Expand All @@ -110,22 +116,31 @@
data
|> prepare_query_params()

request_binance(url, argument_string, method)
request_binance(url, "", argument_string, method)
end

defp validate_credentials(nil, nil),
do: {:error, {:config_missing, "Secret and API key missing"}}

defp validate_credentials("", ""),
do: {:error, {:config_missing, "Secret and API key missing"}}

defp validate_credentials(nil, _api_key),
do: {:error, {:config_missing, "Secret key missing"}}

defp validate_credentials("", _api_key),
do: {:error, {:config_missing, "Secret key missing"}}

defp validate_credentials(_secret_key, nil),
do: {:error, {:config_missing, "API key missing"}}

defp validate_credentials(_secret_key, ""),
do: {:error, {:config_missing, "API key missing"}}

defp validate_credentials(_secret_key, _api_key),
do: :ok

defp parse_response({:ok, response}) do

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (25.3, 1.14.5)

function parse_response/1 is unused

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / testolder (23.3, 1.14)

function parse_response/1 is unused

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (26.1, 1.14.5)

function parse_response/1 is unused

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (25.3, 1.14.5)

function parse_response/1 is unused

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (24.3, 1.14.5)

function parse_response/1 is unused

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (26.1, 1.14.5)

function parse_response/1 is unused

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (24.3, 1.14.5)

function parse_response/1 is unused

Check warning on line 143 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / testolder (23.3, 1.14)

function parse_response/1 is unused
response.body
|> Poison.decode()
|> parse_response_body
Expand All @@ -135,7 +150,7 @@
{:error, {:http_error, err}}
end

defp parse_response_body({:ok, data}) do

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (25.3, 1.14.5)

function parse_response_body/1 is unused

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / testolder (23.3, 1.14)

function parse_response_body/1 is unused

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (26.1, 1.14.5)

function parse_response_body/1 is unused

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (25.3, 1.14.5)

function parse_response_body/1 is unused

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (24.3, 1.14.5)

function parse_response_body/1 is unused

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (26.1, 1.14.5)

function parse_response_body/1 is unused

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / test (24.3, 1.14.5)

function parse_response_body/1 is unused

Check warning on line 153 in lib/binance/rest/http_client.ex

View workflow job for this annotation

GitHub Actions / testolder (23.3, 1.14)

function parse_response_body/1 is unused
case data do
%{"code" => _c, "msg" => _m} = error -> {:error, error}
_ -> {:ok, data}
Expand Down
44 changes: 44 additions & 0 deletions test/binance_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,38 @@ defmodule BinanceTest do
HTTPoison.start()
end

setup do
Application.put_env(:binance, :api_key, "api_key")
Application.put_env(:binance, :secret_key, "api_key")
end

test "ping returns an empty map" do
use_cassette "ping_ok" do
assert Binance.Market.get_ping() == {:ok, %{}}
end
end

test "return err when no key or secret" do
use_cassette "api_key_missing" do
Application.put_env(:binance, :api_key, "")
Application.put_env(:binance, :secret_key, "")

assert Binance.Trade.post_order("LTCBTC", "BUY", "LIMIT", quantity: 0.1, price: 0.01) ==
{:error, {:config_missing, "Secret and API key missing"}}

Application.put_env(:binance, :secret_key, "secret")

assert Binance.Trade.post_order("LTCBTC", "BUY", "LIMIT", quantity: 0.1, price: 0.01) ==
{:error, {:config_missing, "API key missing"}}

Application.put_env(:binance, :secret_key, "")
Application.put_env(:binance, :api_key, "apikey")

assert Binance.Trade.post_order("LTCBTC", "BUY", "LIMIT", quantity: 0.1, price: 0.01) ==
{:error, {:config_missing, "Secret key missing"}}
end
end

test "get_server_time success return an ok, time tuple" do
use_cassette "get_server_time_ok" do
assert Binance.Market.get_time() == {:ok, 1_616_592_268_319}
Expand All @@ -33,6 +59,24 @@ defmodule BinanceTest do
end
end

test "api_key and secret_key are correctly passed to request" do
existing_setting = ExVCR.Setting.get(:filter_request_headers)
ExVCR.Config.filter_request_headers(nil)

use_cassette "get_open_orders_for_api_key_and_secret_key",
match_requests_on: [:headers, :request_body] do
assert {:error, {:binance_error, %{code: -2014, msg: "API-key format invalid."}}} =
Binance.Trade.get_open_orders(api_key: "dummy_api_key", secret_key: "hoge")

Application.put_env(:binance, :api_key, "dummy_api_key")

assert {:error, {:binance_error, %{code: -2014, msg: "API-key format invalid."}}} =
Binance.Trade.get_open_orders()
end

ExVCR.Setting.set(:filter_request_headers, existing_setting)
end

test "get_exchange_info success returns the trading rules and symbol information" do
use_cassette "get_exchange_info_ok" do
assert {:ok, %Binance.Structs.ExchangeInfo{} = info} = Binance.Market.get_exchange_info()
Expand Down
Loading