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

feat: indexing of Celo transactions #9713

Open
wants to merge 52 commits into
base: master
Choose a base branch
from
Open

Conversation

fedor-ivn
Copy link
Collaborator

@fedor-ivn fedor-ivn commented Mar 24, 2024

Motivation

This PR adds support for custom fields from Celo blockchain and implements a
custom way to index Celo transactions.

Changelog

Enhancements

Environment Variables

New value for CHAIN_TYPE variable: celo

New variable: CELO_CORE_CONTRACTS

The CELO network includes several core contracts that are central to its operation. These core contracts manage various aspects of the Celo network, including its stablecoins, governance, identity, and more.

All these contracts can be obtained by querying the Registry contract, which is deployed at the genesis block with the address 0x000000000000000000000000000000000000ce10. This contract serves as a repository for the addresses of all core contracts on the Celo network.

While it's possible to process events from the Registry contract to keep information on core contracts up to date, implementing this in the current Blockscout indexer is not trivial. Fortunately, core contracts are not updated frequently. Thus, once fetched, the addresses typically remain unchanged. To simplify, I decided to store information about core contracts under the CELO_CORE_CONTRACTS variable, which is a JSON object of a specified structure.

In case the contracts are updated, maintaining the Blockscout instance will require updating the CELO_CORE_CONTRACTS variable and manually setting blocks for refetch (all blocks produced after the core contracts' update).

The structure of the JSON value for the CELO_CORE_CONTRACTS variable is detailed in this gist.

The JSON structure in the gist contains two main sections: addresses and events.

  • addresses: This section maps contract names to the list of respective addresses and the block number at which they were updated. For example:
    • "Accounts": [{ "address": "0xed7f51a34b4e71fbe69b3091fcf879cd14bd73a9", "updated_at_block_number": 574 }]
    • "Election": [{ "address": "0x1c3edf937cfc2f6f51784d20deb1af1f9a8655fa", "updated_at_block_number": 592 }]
  • events: This section maps contract addresses to the events associated with them, including any parameters and the block numbers at which these events were emitted. For example:
    • "EpochRewards": { "0xb10ee11244526b94879e1956745ba2e35ae2ba20": { "CarbonOffsettingFundSet": [{ "address": "0x22579ca45ee22e2e16ddf72d955d6cf4c767b0ef", "updated_at_block_number": 15049265 }] } }
    • "FeeHandler": { "0x90d94229623a0a38826a4a7557a6d79acde43f76": { "BurnFractionSet": [{ "updated_at_block_number": 19732579, "value": 0.7999999999999999 }] } }

In the next PR, there is already an implemented mix task that automatically assembles this variable by indexing the entire chain from the 0 to the latest block number.

These changes are also reflected in docs, see blockscout/docs#276.

Transactions

New fields specific to the CELO network are now supported. Here is an outline of how the fields from the RPC node are mapped to the fields in the transactions table:

  • feeCurrencygas_token_contract_address_hash
  • gatewayFeeRecipientgas_fee_recipient_address_hash (this field is scheduled for deprecation as stated in the CELO docs)
  • gatewayFeegateway_fee (this field is scheduled for deprecation as stated in the CELO docs)

The token used for gas payment (feeCurrency) is indexed within the block fetcher process.

Token-Duality Feature

Native chain asset transfers are treated as CELO ERC-20 token transfers. These token transfers are "artificial," meaning there is no actual Transfer event emitted by the CELO ERC-20 contract that corresponds to the token transfer.

⚠️ Important Note: Since there is no actual log associated with such token transfers, these transfers are saved with a negative log index in the token_transfers table.

Indexing of native chain asset transfers as CELO ERC-20 token transfers is handled by the block fetcher and internal transactions fetcher.

API Updates

The following endpoints have been extended to include a celo element in the response, offering additional information about the token used for paying gas:

  • /api/v2/transactions
  • /api/v2/transactions/{transaction_hash}
  • /api/v2/addresses/{address_hash}/transactions
  • /api/v2/main-page/transactions

Example of the celo section in the response:

"celo": {
  "gas_token": {
    "address": "0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1",
    "circulating_market_cap": null,
    "decimals": "18",
    "exchange_rate": null,
    "holders": "3778",
    "icon_url": null,
    "name": "Celo Dollar",
    "symbol": "cUSD",
    "total_supply": "7090075332805485657875961",
    "type": "ERC-20",
    "volume_24h": null
  }
}

Checklist for your Pull Request (PR)

@@ -614,6 +607,39 @@ defmodule Indexer.Block.FetcherTest do
assert fifth_address.fetched_coin_balance_block_number == block_number

EthereumJSONRPC.Nethermind ->
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, the tests in this file just ignore the EthereumJSONRPC variant.

In my case, this config sets the variant geth:

import Config
alias EthereumJSONRPC.Variant
config :indexer, Indexer.Fetcher.Beacon.Blob.Supervisor, disabled?: true
config :indexer, Indexer.Fetcher.Beacon.Blob, start_block: 0
variant = Variant.get()
Code.require_file("#{variant}.exs", "#{__DIR__}/../../../explorer/config/test")
Code.require_file("#{variant}.exs", "#{__DIR__}/../../../indexer/config/test")

So, it loads this config, setting EthereumJSONRPC.Geth variant:

import Config
config :indexer,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.Geth
]

import Config
config :explorer,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.Geth
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.Geth
]

However, these json_rpc_named_arguments settings are just ignored in the tests. Why it could be so?

Copy link
Collaborator Author

@fedor-ivn fedor-ivn Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this, I had nothing to do, but put my celo-changes to EthereumJSONRPC.Nethermind case branch

@fedor-ivn fedor-ivn force-pushed the fi-celo-transactions branch 2 times, most recently from 7c372ce to d16cca3 Compare March 26, 2024 19:25
Copy link
Collaborator Author

@fedor-ivn fedor-ivn Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think hard-coded values are fine at least for v1, because they are really not a subject to change.

For instance, on Celo mainnet, the last transaction to Registry contract was done 4 years ago. On alfajores, Registry contract was not even updated -- e.g it returns nil-address (0x000...) for the core contract names.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow regarding core contracts was updated. Please, read the new PR description

@fedor-ivn fedor-ivn force-pushed the fi-celo-transactions branch 4 times, most recently from 40f044d to a0e8cc3 Compare April 10, 2024 09:22
@fedor-ivn fedor-ivn requested a review from vbaranov April 10, 2024 18:41
Comment on lines 460 to 461
case tokens do
[token] ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not to case token_transfers?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, make sense. Please, check 222feba to verify if I implemented your suggestions correctly

Comment on lines 439 to 449
defp to_address_token_balances(token_transfers, celo_token) do
%{token_transfers: token_transfers}
|> Addresses.extract_addresses()
|> Enum.map(fn %{fetched_coin_balance_block_number: block_number, hash: hash} ->
with {:ok, address_hash} <- Hash.Address.cast(hash),
{:ok, token_contract_address_hash} <- Hash.Address.cast(celo_token.contract_address_hash) do
%{
address_hash: address_hash,
token_contract_address_hash: token_contract_address_hash,
block_number: block_number,
token_type: celo_token.type,
token_id: nil
}
else
error -> Logger.error("Failed to cast string to hash: #{inspect(error)}")
end
end)
|> MapSet.new()
end

defp async_import_celo_token_balances(%{token_transfers: token_transfers, tokens: tokens}) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to define functions in order in which they are called. I.e. firstly define async_import_celo_token_balances and then to_address_token_balances

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also fixed in 222feba

@fedor-ivn fedor-ivn force-pushed the fi-celo-transactions branch 10 times, most recently from 3615ae2 to b6f833b Compare May 18, 2024 12:58
* master: (65 commits)
  fix: Add healthcheck endpoints for indexer-only setup (#10076)
  6.6.0
  fix: Rework revert_reason (#9212)
  fix: Eliminate from_address_hash == #{address_hash} clause for transactions query in case of smart-contracts (#9469)
  feat: Add optional retry of NFT metadata fetch in Indexer.Fetcher.Tok… (#10036)
  fix: Separate indexer setup (#10032)
  feat: Blueprint contracts support (#10058)
  chore: Update hackney pool size: add new fetchers accounting (#9941)
  fix: Disallow batched queries in GraphQL endpoint (#10050)
  feat: Clone with immutable arguments proxy pattern (#10039)
  fix: vyper contracts re-verificaiton (#10053)
  refactor: Refactor get_additional_sources/4 -> get_additional_sources/3 (#10046)
  chore: Bump credo from 1.7.5 to 1.7.6 (#10060)
  chore: Bump redix from 1.5.0 to 1.5.1 (#10059)
  chore: Bump ex_doc from 0.32.1 to 0.32.2 (#10061)
  fix: Fix Unknown UID bug at smart-contract verification (#9986)
  refactor: test database config (#9662)
  chore: remove `has_methods` from `/addresses` (#10051)
  feat: Improve retry NFT fetcher (#10027)
  chore: Add support of Blast-specific L1 OP withdrawal events (#10049)
  ...
@fedor-ivn fedor-ivn added ci:all and removed ci:celo labels May 18, 2024
Comment on lines 74 to 79
# In CELO network, there is a token duality feature where CELO can be used
# as both a native chain currency and as an ERC-20 token. Transactions
# that transfer CELO are also counted as token transfers, and the
# TokenInstance fetcher is called. However, for simplicity, we disable it
# in this test.
Application.put_env(:indexer, Indexer.Fetcher.TokenInstance.Realtime.Supervisor, disabled?: true)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about this workaround?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* master:
  refactor: Remove hardcoded numResults from fetch_pending_transactions_besu (#10117)
  feat: Add window between balance fetch retries for missing balanceOf tokens (#10142)
  fix: Add missing preloads to tokens endpoints (#10072)
  Explicit message on token balance update error (#10129)
  fix: missing nil case for revert reason (#10136)
  fix: Hotfix for Indexer.Fetcher.Optimism.WithdrawalEvent and EthereumJSONRPC.Receipt (#10130)
  hide chain specific fields behind Map.get (#10131)
  --- (#10096)
  feat: indexer for cross level messages on Arbitrum (#9312)
  chore: Bump ecto_sql from 3.11.1 to 3.11.2
  Indexer/API separated images for Redstone
  Update CHANGELOG
  Improve response of address API to return multiple implementations for Diamond proxy (#10113)
  Fix GA pre-release && release workflows
  Update CHANGELOG
  feat: implement fetch_first_trace for Geth (#10087)
  Remove custom release CI for Immutable
  Update CHANGELOG for 6.6.0
  Fix certified flag in the search API v2 endpoint (#10094)
  fix: Update Vyper inner compilers list to support all compilers (#10091)
@fedor-ivn fedor-ivn changed the title feat: index celo transactions feat: indexing of Celo transactions May 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants