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

Handle eth node down type of errors #4471

Merged
merged 2 commits into from
Nov 19, 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
29 changes: 18 additions & 11 deletions lib/sanbase/smart_contracts/uniswap_pair.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,30 @@ defmodule Sanbase.SmartContracts.UniswapPair do
"0x" <> Base.encode16(address, case: :lower)
end

@spec reserves(address) :: {float(), float()}
@spec reserves(address) :: {float(), float()} | {:error, any()}
def reserves(contract) do
[token0_reserves, token1_reserves, _] =
call_contract(
contract,
"getReserves()",
[],
[{:uint, 112}, {:uint, 112}, {:uint, 32}]
)
call_contract(
contract,
"getReserves()",
[],
[{:uint, 112}, {:uint, 112}, {:uint, 32}]
)
|> case do
[token0_reserves, token1_reserves, _] ->
{format_number(token0_reserves, @decimals), format_number(token1_reserves, @decimals)}

{format_number(token0_reserves, @decimals), format_number(token1_reserves, @decimals)}
{:error, _} = error ->
error
end
end

@spec total_supply(address) :: float()
def total_supply(contract) do
[total_supply] = call_contract(contract, "totalSupply()", [], [{:uint, 256}])
format_number(total_supply, @decimals)
call_contract(contract, "totalSupply()", [], [{:uint, 256}])
|> case do
[total_supply] -> format_number(total_supply, @decimals)
{:error, _} -> +0.0
end
end

@spec balance_of(address, address) :: float()
Expand Down
50 changes: 33 additions & 17 deletions lib/sanbase/smart_contracts/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ defmodule Sanbase.SmartContracts.Utils do
)
```
"""
@spec call_contract(address, contract_function, list(), list()) :: any()
@spec call_contract(address, contract_function, list(), list()) :: list() | {:error, any()}
def call_contract(contract, contract_function, args, return_types) do
# https://docs.soliditylang.org/en/latest/abi-spec.html#function-selector-and-argument-encoding

Logger.info(
"[EthNode] Eth call contract with function #{get_function_name(contract_function)}."
)
Expand All @@ -65,14 +63,11 @@ defmodule Sanbase.SmartContracts.Utils do

with {:ok, hex_encoded_binary_response} <-
Ethereumex.HttpClient.eth_call(%{data: "0x" <> function_signature, to: contract}) do
hex_encoded_binary_response
# Strip `0x` prefix
|> String.trim_leading("0x")
|> Base.decode16!(case: :lower)
|> case do
"" -> :error
response -> ABI.TypeDecoder.decode_raw(response, return_types)
end
decode_contract_response(hex_encoded_binary_response, return_types)
else
{:error, reason} = error ->
Logger.warning("Contract call failed: #{inspect(reason)}")
error
end
end

Expand All @@ -92,12 +87,13 @@ defmodule Sanbase.SmartContracts.Utils do
end)

with {:ok, result} <- Ethereumex.HttpClient.batch_request(requests) do
Enum.map(result, fn {:ok, hex_encoded_binary_response} ->
hex_encoded_binary_response
# Strip `0x` prefix
|> String.slice(2..-1//1)
|> Base.decode16!(case: :lower)
|> ABI.TypeDecoder.decode_raw(return_types)
Enum.map(result, fn
{:ok, hex_encoded_binary_response} ->
decode_contract_response(hex_encoded_binary_response, return_types)

{:error, reason} = error ->
Logger.warning("Contract call failed: #{inspect(reason)}")
error
end)
end
end
Expand Down Expand Up @@ -136,4 +132,24 @@ defmodule Sanbase.SmartContracts.Utils do
defp get_function_name(function) when is_binary(function), do: function
defp get_function_name(%{function: function}), do: function
defp get_function_name(function), do: inspect(function) <> "Unexpected"

defp decode_contract_response(hex_encoded_binary_response, return_types) do
hex_encoded_binary_response
# Strip `0x` prefix
|> String.trim_leading("0x")
|> Base.decode16!(case: :lower)
|> case do
"" ->
{:error, :empty_response}

response ->
try do
ABI.TypeDecoder.decode_raw(response, return_types)
rescue
e ->
Logger.warning("Failed to decode contract response: #{inspect(e)}")
{:error, :decode_error}
end
end
end
end