Replace eth_getLogs with public getter

staking
Vadim 5 years ago committed by Victor Baranov
parent 2040ea32eb
commit 4c1a352d75
  1. 200
      apps/block_scout_web/lib/block_scout_web/channels/stakes_channel.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_title.html.eex
  3. 12
      apps/explorer/lib/explorer/staking/contract_reader.ex
  4. 46
      apps/explorer/priv/contracts_abi/posdao/StakingAuRa.json

@ -361,110 +361,102 @@ defmodule BlockScoutWeb.StakesChannel do
def find_claim_reward_pools(socket, staker, staking_contract_address) do def find_claim_reward_pools(socket, staker, staking_contract_address) do
:ets.insert(ContractState, {claim_reward_long_op_key(staker), true}) :ets.insert(ContractState, {claim_reward_long_op_key(staker), true})
try do try do
staker_padded = address_pad_to_64(staker)
json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments) json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
staking_contract = ContractState.get(:staking_contract)
# Search for `PlacedStake` events
{error, pools_staked_into} = find_claim_reward_pools_by_logs(staking_contract_address, [ responses =
# keccak-256 of `PlacedStake(address,address,uint256,uint256)` ContractReader.get_staker_pools_length_request(staker)
"0x2273de02cb1f69ba6259d22c4bc22c60e4c94c193265ef6afee324a04a9b6d22", |> ContractReader.perform_requests(%{staking: staking_contract.address}, staking_contract.abi)
nil, # don't filter by `toPoolStakingAddress` staker_pools_length = responses[:length]
"0x" <> staker_padded # filter by `staker`
], json_rpc_named_arguments) chunk_size = 100
pools = if staker_pools_length > 0 do
# Search for `MovedStake` events chunks = 0..trunc(ceil(staker_pools_length / chunk_size) - 1)
{error, pools_moved_into} = if error == nil do Enum.reduce(chunks, [], fn i, acc ->
find_claim_reward_pools_by_logs(staking_contract_address, [ responses =
# keccak-256 of `MovedStake(address,address,address,uint256,uint256)` ContractReader.get_staker_pools_request(staker, i * chunk_size, chunk_size)
"0x4480d8e4b1e9095b94bf513961d26fe1d32386ebdd103d18fe8738cf4b2223ff", |> ContractReader.perform_requests(%{staking: staking_contract.address}, staking_contract.abi)
nil, # don't filter by `toPoolStakingAddress` acc ++ Enum.map(responses[:pools], fn pool_staking_address ->
"0x" <> staker_padded # filter by `staker` address_bytes_to_string(pool_staking_address)
], json_rpc_named_arguments) end)
end)
else else
{error, []} []
end end
{error, pools} = if error == nil do pools_amounts = Enum.map(pools, fn pool_staking_address ->
pools = Enum.uniq(pools_staked_into ++ pools_moved_into) ContractReader.call_get_reward_amount(
staking_contract_address,
[],
pool_staking_address,
staker,
json_rpc_named_arguments
)
end)
pools_amounts = Enum.map(pools, fn pool_staking_address -> error = Enum.find_value(pools_amounts, fn result ->
ContractReader.call_get_reward_amount( case result do
{:error, reason} -> error_reason_to_string(reason)
_ -> nil
end
end)
{error, pools} = if error != nil do
{error, %{}}
else
block_reward_contract = ContractState.get(:block_reward_contract)
pools =
pools_amounts
|> Enum.map(fn {_, amounts} -> amounts end)
|> Enum.zip(pools)
|> Enum.filter(fn {amounts, _} -> amounts.token_reward_sum > 0 || amounts.native_reward_sum > 0 end)
|> Enum.map(fn {amounts, pool_staking_address} ->
responses =
ContractReader.epochs_to_claim_reward_from_request(pool_staking_address, staker)
|> ContractReader.perform_requests(%{block_reward: block_reward_contract.address}, block_reward_contract.abi)
epochs =
array_to_ranges(responses[:epochs])
|> Enum.map(fn {first, last} ->
Integer.to_string(first) <> (if first != last, do: "-" <> Integer.to_string(last), else: "")
end)
data = Map.put(amounts, :epochs, Enum.join(epochs, ","))
{data, pool_staking_address}
end)
|> Enum.filter(fn {data, _} -> data.epochs != "" end)
pools_gas_estimates = Enum.map(pools, fn {_data, pool_staking_address} ->
result = ContractReader.claim_reward_estimate_gas(
staking_contract_address, staking_contract_address,
[], [],
pool_staking_address, pool_staking_address,
staker, staker,
json_rpc_named_arguments json_rpc_named_arguments
) )
{pool_staking_address, result}
end) end)
error = Enum.find_value(pools_amounts, fn result -> error = Enum.find_value(pools_gas_estimates, fn {_, result} ->
case result do case result do
{:error, reason} -> error_reason_to_string(reason) {:error, reason} -> error_reason_to_string(reason)
_ -> nil _ -> nil
end end
end) end)
{error, pools} = if error != nil do pools = if error == nil do
{error, %{}} pools_gas_estimates = Map.new(pools_gas_estimates)
else Map.new(pools, fn {data, pool_staking_address} ->
block_reward_contract = ContractState.get(:block_reward_contract) {:ok, estimate} = pools_gas_estimates[pool_staking_address]
data = Map.put(data, :gas_estimate, estimate)
pools = {pool_staking_address, data}
pools_amounts
|> Enum.map(fn {_, amounts} -> amounts end)
|> Enum.zip(pools)
|> Enum.filter(fn {amounts, _} -> amounts.token_reward_sum > 0 || amounts.native_reward_sum > 0 end)
|> Enum.map(fn {amounts, pool_staking_address} ->
responses =
ContractReader.epochs_to_claim_reward_from_request(pool_staking_address, staker)
|> ContractReader.perform_requests(%{block_reward: block_reward_contract.address}, block_reward_contract.abi)
epochs =
array_to_ranges(responses[:epochs])
|> Enum.map(fn {first, last} ->
Integer.to_string(first) <> (if first != last, do: "-" <> Integer.to_string(last), else: "")
end)
data = Map.put(amounts, :epochs, Enum.join(epochs, ","))
{data, pool_staking_address}
end)
|> Enum.filter(fn {data, _} -> data.epochs != "" end)
pools_gas_estimates = Enum.map(pools, fn {_data, pool_staking_address} ->
result = ContractReader.claim_reward_estimate_gas(
staking_contract_address,
[],
pool_staking_address,
staker,
json_rpc_named_arguments
)
{pool_staking_address, result}
end) end)
else
error = Enum.find_value(pools_gas_estimates, fn {_, result} -> %{}
case result do
{:error, reason} -> error_reason_to_string(reason)
_ -> nil
end
end)
pools = if error == nil do
pools_gas_estimates = Map.new(pools_gas_estimates)
Map.new(pools, fn {data, pool_staking_address} ->
{:ok, estimate} = pools_gas_estimates[pool_staking_address]
data = Map.put(data, :gas_estimate, estimate)
{pool_staking_address, data}
end)
else
%{}
end
{error, pools}
end end
{error, pools} {error, pools}
else
{error, %{}}
end end
html = View.render_to_string( html = View.render_to_string(
@ -548,47 +540,7 @@ defmodule BlockScoutWeb.StakesChannel do
end end
end end
defp find_claim_reward_pools_by_logs(staking_contract_address, topics, json_rpc_named_arguments) do defp address_bytes_to_string(hash), do: "0x" <> Base.encode16(hash, case: :lower)
latest_block = BlockNumber.get_max()
split_by = 500 # must be less than 1000
iterations = 0..trunc(ceil(latest_block / split_by) - 1)
Enum.reduce(iterations, {nil, []}, fn i, acc ->
{acc_error, acc_pools} = acc
if acc_error do
{acc_error, []}
else
from = i * split_by + 1
to = (i + 1) * split_by
to = if to > latest_block, do: latest_block, else: to
result = EthereumJSONRPC.request(%{
id: 0,
method: "eth_getLogs",
params: [%{
fromBlock: "0x" <> Integer.to_string(from, 16),
toBlock: "0x" <> Integer.to_string(to, 16),
address: staking_contract_address,
topics: topics
}]
}) |> EthereumJSONRPC.json_rpc(json_rpc_named_arguments)
case result do
{:ok, response} ->
pools = Enum.uniq(acc_pools ++ Enum.map(response, fn event ->
truncate_address(Enum.at(event["topics"], 1))
end))
{acc_error, pools}
{:error, reason} ->
{error_reason_to_string(reason), []}
end
end
end)
end
defp address_pad_to_64(address) do
address
|> String.replace_leading("0x", "")
|> String.pad_leading(64, ["0"])
end
defp array_to_ranges(numbers, prev_ranges \\ []) do defp array_to_ranges(numbers, prev_ranges \\ []) do
length = Enum.count(numbers) length = Enum.count(numbers)
@ -647,8 +599,4 @@ defmodule BlockScoutWeb.StakesChannel do
staker = if staker == nil, do: "", else: staker staker = if staker == nil, do: "", else: staker
Atom.to_string(@claim_reward_long_op) <> "_" <> staker Atom.to_string(@claim_reward_long_op) <> "_" <> staker
end end
defp truncate_address("0x000000000000000000000000" <> truncated_address) do
"0x#{truncated_address}"
end
end end

@ -11,7 +11,7 @@
<div class="check card-title-control"> <div class="check card-title-control">
<input type="checkbox" pool-filter-my /> <input type="checkbox" pool-filter-my />
<div class="check-icon"></div> <div class="check-icon"></div>
<div class="check-text"><%= gettext("Show only those I staked into") %></div> <div class="check-text"><%= gettext("Show only those I have stake in") %></div>
</div> </div>
</div> </div>
</div> </div>

@ -162,6 +162,18 @@ defmodule Explorer.Staking.ContractReader do
] ]
end end
def get_staker_pools_request(staker, offset, length) do
[
pools: {:staking, "getStakerPools", [staker, offset, length]}
]
end
def get_staker_pools_length_request(staker) do
[
length: {:staking, "getStakerPoolsLength", [staker]}
]
end
def pool_staking_requests(staking_address, block_number) do def pool_staking_requests(staking_address, block_number) do
[ [
active_delegators: active_delegators_request(staking_address, block_number)[:active_delegators], active_delegators: active_delegators_request(staking_address, block_number)[:active_delegators],

@ -36,6 +36,52 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"constant": true,
"inputs": [
{
"name": "_staker",
"type": "address"
},
{
"name": "_offset",
"type": "uint256"
},
{
"name": "_length",
"type": "uint256"
}
],
"name": "getStakerPools",
"outputs": [
{
"name": "result",
"type": "address[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_staker",
"type": "address"
}
],
"name": "getStakerPoolsLength",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{ {
"constant": true, "constant": true,
"inputs": [ "inputs": [

Loading…
Cancel
Save