{"id":37031347,"url":"https://github.com/aboutcircles/circles-nethermind-plugin","last_synced_at":"2026-03-17T15:33:50.176Z","repository":{"id":228568829,"uuid":"774324966","full_name":"aboutcircles/circles-nethermind-plugin","owner":"aboutcircles","description":null,"archived":false,"fork":false,"pushed_at":"2026-01-12T15:06:41.000Z","size":41437,"stargazers_count":7,"open_issues_count":3,"forks_count":4,"subscribers_count":1,"default_branch":"dev","last_synced_at":"2026-01-12T15:41:53.604Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aboutcircles.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-03-19T10:57:44.000Z","updated_at":"2026-01-12T15:06:46.000Z","dependencies_parsed_at":"2025-12-17T21:01:01.364Z","dependency_job_id":null,"html_url":"https://github.com/aboutcircles/circles-nethermind-plugin","commit_stats":null,"previous_names":["aboutcircles/circles-nethermind-plugin"],"tags_count":83,"template":false,"template_full_name":null,"purl":"pkg:github/aboutcircles/circles-nethermind-plugin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aboutcircles%2Fcircles-nethermind-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aboutcircles%2Fcircles-nethermind-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aboutcircles%2Fcircles-nethermind-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aboutcircles%2Fcircles-nethermind-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aboutcircles","download_url":"https://codeload.github.com/aboutcircles/circles-nethermind-plugin/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aboutcircles%2Fcircles-nethermind-plugin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-14T03:50:26.734Z","updated_at":"2026-01-14T03:50:27.440Z","avatar_url":"https://github.com/aboutcircles.png","language":"C#","funding_links":[],"categories":["APIs \u0026 Infrastructure"],"sub_categories":["Developer Demos"],"readme":"# Circles Nethermind Plug-in\n\nA [Nethermind](https://www.nethermind.io/nethermind-client) plugin to index and\nquery [Circles](https://www.aboutcircles.com/) protocol events.\n\n* [Quickstart](#quickstart)\n    * [Query a node](#query-a-node)\n    * [Run a node](#run-a-node)\n        * [Clone the repository](#1-clone-the-repository)\n        * [Create a jwtsecret](#2-create-a-jwtsecret)\n        * [Set up the .env file](#3-set-up-the-env-file)\n        * [Run node](#4-run-node)\n            * [Ports](#ports)\n            * [Volumes](#volumes)\n* [Circles RPC methods](#circles-rpc-methods)\n    * [circles_getTotalBalance / circlesV2_getTotalBalance](#circles_gettotalbalance--circlesv2_gettotalbalance)\n    * [circles_getTokenBalances](#circles_gettokenbalances)\n    * [circles_query](#circles_query)\n    * [circles_events](#circles_events)\n    * [eth_subscribe(\"circles\")](#eth_subscribecircles)\n    * [circlesV2_findPath](#circlesv2_findpath)\n    * [circles_getTrustRelations](#circles_gettrustrelations)\n    * [circles_getCommonTrust](#circles_getcommontrust)\n    * [circles_getAvatarInfo / circles_getAvatarInfoBatch](#circles_getavatarinfo--circles_getavatarinfobatch)\n    * [circles_getProfileCid / circles_getProfileCidBatch](#circles_getprofilecid--circles_getprofilecidbatch)\n    * [circles_getProfileByCid / circles_getProfileByCidBatch](#circles_getprofilebycid--circles_getprofilecidbybatch)\n    * [circles_getProfileByAddress / circles_getProfileByAddressBatch](#circles_getprofilebyaddress--circles_getprofilebyaddressbatch)\n    * [circles_getTokenInfo / circles_getTokenInfoBatch](#circles_gettokeninfo--circles_gettokeninfobatch)\n    * [circles_tables](#circles_tables)\n    * [circles_health](#circles_health)\n    * [circles_getNetworkSnapshot](#circles_getnetworksnapshot)\n    * [circles_searchProfiles](#circles_searchprofiles)\n* [Add a custom protocol](#add-a-custom-protocol)\n    * [DatabaseSchema.cs](#databaseschemacs)\n        * [Tables](#tables)\n        * [EventDtoTableMap](#eventdtotablemap)\n        * [SchemaPropertyMap](#schemapropertymap)\n    * [Events.cs](#eventscs)\n    * [LogParser.cs](#logparsercs)\n    * [Register the protocol](#register-the-protocol)\n        * [Register the schema](#register-the-schema)\n        * [Register the SchemaPropertyMap and EventDtoTableMap](#register-the-schemapropertymap-and-eventdtotablemap)\n        * [Register the LogParser](#register-the-logparser)\n\n## Quickstart\n\n### Query a node\n\nIf you're just looking for a way to query Circles events, you can check out the query examples:\n\n* [General examples](general-example-requests.md)\n* [Circles v1 examples](v1-example-requests.md)\n* [Circles v2 examples](v2-example-requests.md)\n\nFor a detailed description of the available RPC methods, see the [Circles RPC methods](#circles-rpc-methods) section.\n\n### Run a node\n\nThe repository contains a docker-compose file to start a Nethermind node with the Circles plugin installed. There are\nconfigurations for Gnosis Chain and Chiado.\n\nThe quickstart configurations use [lighthouse](https://github.com/sigp/lighthouse) as consensus engine and spin up a\npostgres database to store the indexed data.\n\n#### 1. Clone the repository\n\n```bash\ngit clone https://github.com/CirclesUBI/circles-nethermind-plugin.git\ncd circles-nethermind-plugin\n```\n\n#### 2. Create a jwtsecret\n\nFor the use with Gnosis Chain and Chiado, a shared secret is required to authenticate requests between the execution and\nconsensus engine.\n\n```bash\n# Gnosis Chain\nmkdir -p ./.state/jwtsecret-gnosis\nopenssl rand -hex 32 \u003e ./.state/jwtsecret-gnosis/jwt.hex\n```\n\n```bash\n# Chiado\nmkdir -p ./.state/jwtsecret-chiado\nopenssl rand -hex 32 \u003e ./.state/jwtsecret-chiado/jwt.hex\n```\n\n#### 3. Set up the .env file\n\nCopy the `.env.example` file to `.env` and adjust the values to your needs.\n\n```bash\ncp .env.example .env\n```\n\n#### 4. Run node\n\nChoose if your want to run your node on Gnosis Chain or Chiado.\n\n```bash\n# Gnosis chain\ndocker compose -f docker-compose.gnosis.yml up -d\n```\n\n```bash\n# Chiado\ndocker compose -f docker-compose.chiado.yml up -d\n```\n\nThat's it! The node must be fully synced before you can start querying the Circles events.\nOnce synced you can use the node like any other RPC node, but with the added benefit of querying Circles events directly\nat the same RPC endpoint.\n\n##### Ports:\n\n* `30303/tcp` (nethermind p2p)\n* `30303/udp` (nethermind p2p)\n* `8545/tcp` (nethermind rpc)\n* `5432/tcp` (postgres)\n* `9000/tcp` (consensus p2p)\n* `9000/udp` (consensus p2p)\n* `5054/tcp` (consensus metrics)\n\n##### Volumes\n\n* `./.state` - Directory containing all host mapped docker volumes\n    * `./.state/consensus-chiado|consensus-chiado` - Lighthouse consensus engine data\n    * `./.state/nethermind-chiado|nethermind-gnosis` - Nethermind data\n    * `./.state/postgres-chiado|postgres-gnosis` - Postgres data\n    * `./.state/jwtsecret-chiado|jwtsecret-gnosis` - Shared secret between execution and consensus engine\n\n\n## Circles RPC methods\n\nThe plugin extends the Nethermind JSON-RPC API with additional methods to query Circles events and aggregate values.\n\nYou can find concrete examples for all rpc-methods in the [v1-example-requests.md](v1-example-requests.md)\nand [v2-example-requests.md](v2-example-requests.md) files.\n\n### circles_getTotalBalance / circlesV2_getTotalBalance\n\nThese methods allow you to query the total Circles (v1/v2) holdings of an address.\n\n**Signature**:\n\n* `circles_getTotalBalance(address: string, asTimeCircles: bool = false)`.\n* `circlesV2_getTotalBalance(address: string, asTimeCircles: bool = false)`.\n\n#### Example V1\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getTotalBalance\",\n  \"params\": [\n    \"0xde374ece6fa50e781e81aac78e811b33d16912c7\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Example V2\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circlesV2_getTotalBalance\",\n  \"params\": [\n    \"0xde374ece6fa50e781e81aac78e811b33d16912c7\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nThis method returns a string formatted BigInteger value. The value is the sum of all Circles holdings of the address.\n\nIf `asTimeCircles` is set to `true`, the value is formatted\nas [TimeCircles](https://github.com/CirclesUBI/timecircles) floating point number instead of the raw BigInteger value.\n\n### circles_getTokenBalances\n\nThese methods allow you to query all individual Circles (v1/v2) holdings of an address.\n\n**Signature**:\n\n* `circles_getTokenBalances(address: string, asTimeCircles: bool = false)`.\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getTokenBalances\",\n  \"params\": [\n    \"0xd68193591d47740e51dfbc410da607a351b56586\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nThis method returns an array of objects with the following properties:\n\n* `tokenId` - The address of the token.\n* `balance` - The balance of the token.\n* `tokenOwner` - The address of the token owner.\n\nIf `asTimeCircles` is set to `true`, the value is formatted\nas [TimeCircles](https://github.com/CirclesUBI/timecircles) floating point number instead of the raw BigInteger value.\n\n### circles_query\n\nThis method allows you to query Circles events. The method takes a single parameter, which is a JSON object with the\nfollowing properties:\n\n* `namespace` - The protocol namespace to query (System, CrcV1 or CrcV2).\n* `table` - The table to query (e.g. `Signup`, `Trust`, etc.).\n* `columns` - An array of column names to return or `[]` to return all columns of the table.\n* `filter` - Filters that can be used e.g. for pagination or to search for specific values.\n* `order` - A list of columns to order the results by.\n* `distinct` - If set to `true`, only distinct rows are returned.\n* `limit` - The maximum number of rows to return (defaults to max. 1000).\n\n_NOTE: There is no default order, so make sure to always add sensible order columns._\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_query\",\n  \"params\": [\n    {\n      \"Namespace\": \"V_Crc\",\n      \"Table\": \"Avatars\",\n      \"Limit\": 100,\n      \"Columns\": [],\n      \"Filter\": [],\n      \"Order\": [\n        {\n          \"Column\": \"blockNumber\",\n          \"SortOrder\": \"DESC\"\n        },\n        {\n          \"Column\": \"transactionIndex\",\n          \"SortOrder\": \"DESC\"\n        },\n        {\n          \"Column\": \"logIndex\",\n          \"SortOrder\": \"DESC\"\n        }\n      ]\n    }\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nThe result is a JSON object that resembles a table with rows and columns:\n\n* `Columns` - An array of column names.\n* `Rows` - An array of rows, where each row is an array of values.\n\n#### Available namespaces, tables and columns\n\nEvery table has at least the following columns:\n\n* `blockNumber` - The block number the event was emitted in.\n* `timestamp` - The unix timestamp of the event.\n* `transactionIndex` - The index of the transaction in the block.\n* `logIndex` - The index of the log in the transaction.\n\nTables for batch events have an additional `batchIndex` column.\nThe items of a batch are treated like individual events that can only be distinguished by the `batchIndex`.\n\nNamespaces and tables:\n\n## Available Namespaces, Tables, and Columns\n\nEvery table has at least the following columns:\n* `blockNumber` - The block number the event was emitted in.\n* `timestamp` - The unix timestamp of the event.\n* `transactionIndex` - The index of the transaction in the block.\n* `logIndex` - The index of the log in the transaction.\n* `transactionHash` - The hash of the transaction.\n\nTables for batch events have an additional `batchIndex` column.\n\n### Namespaces and Tables:\n\n#### CrcV1\n* `HubTransfer`\n    * `from` (Address)\n    * `to` (Address)\n    * `amount` (BigInteger)\n* `Signup`\n    * `user` (Address)\n    * `token` (Address)\n* `OrganizationSignup`\n    * `organization` (Address)\n* `Trust`\n    * `canSendTo` (Address)\n    * `user` (Address)\n    * `limit` (BigInteger)\n* `Transfer`\n    * `tokenAddress` (Address)\n    * `from` (Address)\n    * `to` (Address)\n    * `amount` (BigInteger)\n\n#### CrcV2\n* `PersonalMint`\n    * `human` (Address)\n    * `amount` (BigInteger)\n    * `startPeriod` (BigInteger)\n    * `endPeriod` (BigInteger)\n* `RegisterGroup`\n    * `group` (Address)\n    * `mint` (Address)\n    * `treasury` (Address)\n    * `name` (String)\n    * `symbol` (String)\n* `RegisterHuman`\n    * `avatar` (Address)\n* `RegisterOrganization`\n    * `organization` (Address)\n    * `name` (String)\n* `Stopped`\n    * `avatar` (Address)\n* `Trust`\n    * `truster` (Address)\n    * `trustee` (Address)\n    * `expiryTime` (BigInteger)\n* `TransferSingle`\n    * `operator` (Address)\n    * `from` (Address)\n    * `to` (Address)\n    * `id` (BigInteger)\n    * `value` (BigInteger)\n* `TransferBatch`\n    * `operator` (Address)\n    * `from` (Address)\n    * `to` (Address)\n    * `id` (BigInteger)\n    * `value` (BigInteger)\n* `URI`\n    * `value` (String)\n    * `id` (BigInteger)\n* `ApprovalForAll`\n    * `account` (Address)\n    * `operator` (Address)\n    * `approved` (Boolean)\n* `Erc20WrapperDeployed`\n    * `avatar` (Address)\n    * `erc20Wrapper` (Address)\n    * `circlesType` (Int64)\n* `Erc20WrapperTransfer`\n    * `tokenAddress` (Address)\n    * `from` (Address)\n    * `to` (Address)\n    * `amount` (BigInteger)\n* `DepositInflationary`\n    * `account` (Address)\n    * `amount` (BigInteger)\n    * `demurragedAmount` (BigInteger)\n* `WithdrawInflationary`\n    * `account` (Address)\n    * `amount` (BigInteger)\n    * `demurragedAmount` (BigInteger)\n* `DepositDemurraged`\n    * `account` (Address)\n    * `amount` (BigInteger)\n    * `inflationaryAmount` (BigInteger)\n* `WithdrawDemurraged`\n    * `account` (Address)\n    * `amount` (BigInteger)\n    * `inflationaryAmount` (BigInteger)\n* `StreamCompleted`\n    * `operator` (Address)\n    * `from` (Address)\n    * `to` (Address)\n    * `id` (BigInteger)\n    * `amount` (BigInteger)\n\n#### CrcV2 (NameRegistry)\n* `RegisterShortName`\n    * `avatar` (Address)\n    * `shortName` (UInt72)\n    * `nonce` (BigInteger)\n* `UpdateMetadataDigest`\n    * `avatar` (Address)\n    * `metadataDigest` (Bytes32)\n* `CidV0`\n    * `avatar` (Address)\n    * `cidV0Digest` (Bytes32)\n\n#### CrcV2 (StandardTreasury)\n* `CreateVault`\n    * `group` (Address)\n    * `vault` (Address)\n* `CollateralLockedSingle`\n    * `group` (Address)\n    * `id` (BigInteger)\n    * `value` (BigInteger)\n    * `userData` (Bytes)\n* `CollateralLockedBatch`\n    * `group` (Address)\n    * `id` (BigInteger)\n    * `value` (BigInteger)\n    * `userData` (Bytes)\n* `GroupRedeem`\n    * `group` (Address)\n    * `id` (BigInteger)\n    * `value` (BigInteger)\n    * `data` (Bytes)\n* `GroupRedeemCollateralReturn`\n    * `group` (Address)\n    * `to` (Address)\n    * `id` (BigInteger)\n    * `value` (BigInteger)\n* `GroupRedeemCollateralBurn`\n    * `group` (Address)\n    * `id` (BigInteger)\n    * `value` (BigInteger)\n\n#### V_CrcV1 (Circles v1 views)\n    * `Avatars` (view combining `Signup` and `OrganizationSignup`)\n    * `TrustRelations` (view filtered to represent all current `Trust` relations)\n#### V_CrcV2 (Circles v2 views)\n    * `Avatars` (view combining `CrcV2_RegisterHuman`, `CrcV2_RegisterGroup` and\n      `CrcV2_RegisterOrganization`)\n    * `TrustRelations` (view filtered to represent all current `Trust` relations)\n    * `Transfers` (view combining `CrcV2_TransferBatch`, `CrcV2_TransferSingle` and `CrcV2_Erc20WrapperTransfer`)\n    * `GroupMemberships` (view combining `CrcV2_RegisterGroup`, `V_CrcV2_TrustRelations` and `V_CrcV2_Avatars`)\n#### V_Crc (Views combining v1 and v2 data)\n    * `Avatars` (view combining `V_CrcV1_Avatars` and `V_CrcV2_Avatars`)\n    * `TrustRelations` (view combining `V_CrcV1_TrustRelations` and `V_CrcV2_TrustRelations`)\n    * `Transfers` (view combining `V_CrcV1_Transfer` and `V_CrcV2_Transfers`)\n\n#### Available filter types\n\n* `Equals`\n* `NotEquals`\n* `GreaterThan`\n* `GreaterThanOrEquals`\n* `LessThan`\n* `LessThanOrEquals`\n* `Like`\n* `NotLike`\n* `In`\n* `NotIn`\n\n#### Pagination\n\nYou can use the combination of `blockNumber`, `transactionIndex` and `logIndex`\n(+ `batchIndex` in the case of batch events) together with a `limit` and order to paginate through the results.\n\n### circles_events\n\nQueries all events that involve a specific address. Can be used to e.g. easily populate a user's transaction history.\n\n**Signature**: `circles_events(address: string, fromBlock: number, toBlock?: number)`.\n\nThe `fromBlock` and `toBlock` parameters can be used to filter the events by block number.\nThe `toBlock` parameter can be set to `null` to query all events from `fromBlock` to the latest block.\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_events\",\n  \"params\": [\n    \"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\n    30282299,\n    null\n  ]\n}' -H \"Content-Type: application/json\" https://rpc.helsinki.aboutcircles.com/\n```\n\n#### Response\n\nThe response generally contains the following fields:\n\n* `event` - The name of the event (See\n  [Available namespaces, tables and columns](#available-namespaces-tables-and-columns) for available event types).\n* `values` - The values of the event.\n\nThe values contain at least the following fields:\n\n* `blockNumber` - The block number the event was emitted in.\n* `timestamp` - The unix timestamp of the event.\n* `transactionIndex` - The index of the transaction in the block.\n* `logIndex` - The index of the log in the transaction.\n* `transactionHash` - The hash of the transaction.\n\n#### Filtering\n\nThe `circles_events` method can be filtered by specifying a filter as the last parameter:\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_events\",\n  \"params\": [\n    null,\n    0,\n    null,\n    [{\n       \"Type\": \"FilterPredicate\",\n       \"FilterType\": \"In\",\n       \"Column\": \"transactionHash\",\n       \"Value\": [\"0xd1380076b6ad2d1872951da1852c20ed3161e10f237aca27dc531795fa6867e0\"]\n    }]\n  ]\n}' -H \"Content-Type: application/json\" https://rpc.helsinki.aboutcircles.com/\n```\n\n### eth_subscribe(\"circles\")\n\nSubscribes to all Circles events. The subscription is a stream of events that are emitted as soon as they've been\nindexed. Can be filtered to just a specific address.\n\n**Signature**: `eth_subscribe(\"circles\", { address?: string })`.\n\n#### Example\n\nThis call subscribes to all Circles events (firehose):\n\n```shell\nnpx wscat -c wss://rpc.helsinki.aboutcircles.com/ws -x '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_subscribe\",\"params\":[\"circles\",{}]}' -w 3600\n```\n\nThis call subscribes to all Circles events that involve the address `0xde374ece6fa50e781e81aac78e811b33d16912c7`:\n\n```shell\nnpx wscat -c wss://rpc.helsinki.aboutcircles.com/ws -x '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_subscribe\",\"params\":[\"circles\",{\"address\":\"0xde374ece6fa50e781e81aac78e811b33d16912c7\"}]}' -w 3600\n```\n\n#### Response\n\nThe emitted events are the same as the objects returned by the `circles_events` ([circles_events Response](#response-1))\nmethod.\n\n### circlesV2_findPath\n\nFinds a transitive transfer path between two addresses in the Circles V2 graph. Can be used to calculate paths for swaps and payments.\n\n**Signature**: `circlesV2_findPath(flowRequest: FlowRequest)`.\n\nThe `FlowRequest` object has the following properties:\n\n* `Source` - The address initiating the transfer\n* `Sink` - The target address\n* `TargetFlow` - The desired amount to transfer (as a string)\n* `FromTokens` - (Optional) Array of token addresses to use as sources\n* `ToTokens` - (Optional) Array of token addresses to accept as destinations\n* `SimulatedBalances` - (Optional) Array of balance overrides for simulation\n\n#### Example - Standard Path\n\n```shell\ncurl 'http://localhost:8545/' \\\n -H 'Content-Type: application/json' \\\n --data-raw '{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"circlesV2_findPath\",\"params\":[{\"Source\":\"0x749c930256b47049cb65adcd7c25e72d5de44b3b\",\"Sink\":\"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\"TargetFlow\":\"99999999999999999999999999999999999\"}]}'\n```\n\n#### Example - Token Swap\n\n```shell\ncurl 'http://localhost:8545/' \\\n  -H 'Content-Type: application/json' \\\n  --data-raw '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 0,\n  \"method\": \"circlesV2_findPath\",\n  \"params\": [\n    {\n      \"Source\": \"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\n      \"Sink\": \"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\n      \"TargetFlow\": \"100000000000000000000\",\n      \"FromTokens\": [\n        \"0x86533d1aDA8Ffbe7b6F7244F9A1b707f7f3e239b\"\n      ],\n      \"ToTokens\": [\n        \"0x6B69683C8897e3d18e74B1Ba117b49f80423Da5d\"\n      ],\n      \"SimulatedBalances\": [\n        {\n          \"Holder\": \"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\n          \"Token\": \"0x86533d1aDA8Ffbe7b6F7244F9A1b707f7f3e239b\",\n          \"Amount\": \"100000000000000000000\",\n          \"IsWrapped\": false\n        }\n      ]\n    }\n  ]\n}'\n```\n\n#### Response\n\nReturns a `MaxFlowResponse` object containing the calculated transfer path and flow information.\n\n### circles_getTrustRelations\n\nGets all trust relations for a specific address.\n\n**Signature**: `circles_getTrustRelations(address: string)`.\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getTrustRelations\",\n  \"params\": [\n    \"0xde374ece6fa50e781e81aac78e811b33d16912c7\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns an object with the following properties:\n\n* `User` - The queried address\n* `Trusts` - Array of addresses that the user trusts with their trust limits\n* `TrustedBy` - Array of addresses that trust the user\n\n### circles_getCommonTrust\n\nGets the common trust relations between two addresses. Only considers common outgoing trust relations.\n\n**Signature**: `circles_getCommonTrust(address1: string, address2: string, version?: int)`.\n\nThe optional `version` parameter can be used to filter by Circles version (1 or 2). If not specified, returns trusts from both versions.\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getCommonTrust\",\n  \"params\": [\n    \"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\n    \"0xe8fc7a2d0573e5164597b05f14fa9a7fca7b215c\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns an array of addresses that both users have outgoing trust relations to.\n\n### circles_getAvatarInfo / circles_getAvatarInfoBatch\n\nGets essential information about one or more Circles avatars.\n\n**Signature**:\n\n* `circles_getAvatarInfo(address: string)`\n* `circles_getAvatarInfoBatch(addresses: string[])`\n\n#### Example - Single Avatar\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getAvatarInfo\",\n  \"params\": [\n    \"0xde374ece6fa50e781e81aac78e811b33d16912c7\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Example - Batch Query\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getAvatarInfoBatch\",\n  \"params\": [\n    [\n      \"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\n      \"0xc3a1428c04c426cdf513c6fc8e09f55ddaf50cd7\",\n      \"0xf712d3b31de494b5c0ea51a6a407460ca66b12e8\"\n    ]\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns an `AvatarRow` (or array of `AvatarRow?`) containing:\n\n* `Version` - Active Circles version (1 or 2)\n* `Type` - Avatar type (e.g., 'CrcV2_RegisterHuman', 'CrcV2_RegisterGroup', etc.)\n* `Avatar` - The avatar address\n* `TokenId` - The token address or encoded token ID\n* `HasV1` - Whether the avatar is signed up at v1\n* `V1Token` - The v1 token address (if applicable)\n* `CidV0Digest` - Metadata CIDv0 bytes\n* `CidV0` - The CIDv0 string\n* `IsHuman` - Whether the avatar is a human\n* `Name` - Group name (if applicable)\n* `Symbol` - Group symbol (if applicable)\n\n### circles_getProfileCid / circles_getProfileCidBatch\n\nGets the profile CID for one or more avatar addresses.\n\n**Signature**:\n\n* `circles_getProfileCid(address: string)`\n* `circles_getProfileCidBatch(addresses: string[])`\n\n#### Example - Single Address\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getProfileCid\",\n  \"params\": [\n    \"0xde374ece6fa50e781e81aac78e811b33d16912c7\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Example - Batch Query\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getProfileCidBatch\",\n  \"params\": [\n    [\n      \"0xde374ece6fa50e781e81aac78e811b33d16912c7\",\n      \"0xc3a1428c04c426cdf513c6fc8e09f55ddaf50cd7\"\n    ]\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns the CIDv0 string for the profile, or `null` if not found.\n\n### circles_getProfileByCid / circles_getProfileByCidBatch\n\nGets profile data by CID (or multiple CIDs).\n\n**Signature**:\n\n* `circles_getProfileByCid(cid: string)`\n* `circles_getProfileByCidBatch(cids: string[])`\n\n#### Example - Single CID\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getProfileByCid\",\n  \"params\": [\n    \"Qmb2s3hjxXXcFqWvDDSPCd1fXXa9gcFJd8bzdZNNAvkq9W\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Example - Batch Query\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getProfileByCidBatch\",\n  \"params\": [\n    [\n      \"Qmb2s3hjxXXcFqWvDDSPCd1fXXa9gcFJd8bzdZNNAvkq9W\",\n      \"QmZuR1Jkhs9RLXVY28eTTRSnqbxLTBSoggp18Yde858xCM\"\n    ]\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns a `Profile` object (or array of `Profile?`) containing:\n\n* `address` - Avatar address\n* `CID` - The profile CID\n* `lastUpdatedAt` - Unix timestamp of last update\n* `name` - Profile name\n* `description` - Profile description\n* `registeredName` - Registered short name (if any)\n* `location` - Profile location\n* `imageUrl` - Avatar image URL\n* `previewImageUrl` - Preview image URL\n* `geoLocation` - Geo coordinates [longitude, latitude]\n* `longitude` - Longitude coordinate\n* `latitude` - Latitude coordinate\n* `shortName` - Short name\n* `avatarType` - Avatar type\n\n### circles_getProfileByAddress / circles_getProfileByAddressBatch\n\nGets profile data by avatar address (or multiple addresses).\n\n**Signature**:\n\n* `circles_getProfileByAddress(avatar: string)`\n* `circles_getProfileByAddressBatch(avatars: string[])`\n\n#### Example - Single Address\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getProfileByAddress\",\n  \"params\": [\n    \"0xc3a1428c04c426cdf513c6fc8e09f55ddaf50cd7\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Example - Batch Query\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getProfileByAddressBatch\",\n  \"params\": [\n    [\n      \"0xc3a1428c04c426cdf513c6fc8e09f55ddaf50cd7\",\n      \"0xde374ece6fa50e781e81aac78e811b33d16912c7\"\n    ]\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns a `Profile` object (or array of `Profile?`) with the same structure as `circles_getProfileByCid`.\n\n### circles_getTokenInfo / circles_getTokenInfoBatch\n\nGets token metadata for one or more token addresses.\n\n**Signature**:\n\n* `circles_getTokenInfo(tokenAddress: string)`\n* `circles_getTokenInfoBatch(tokenAddresses: string[])`\n\n#### Example - Single Token\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getTokenInfo\",\n  \"params\": [\n    \"0x0d8c4901dd270fe101b8014a5dbecc4e4432eb1e\"\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Example - Batch Query\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getTokenInfoBatch\",\n  \"params\": [\n    [\n      \"0x0d8c4901dd270fe101b8014a5dbecc4e4432eb1e\",\n      \"0x42cedde51198d1773590311e2a340dc06b24cb37\"\n    ]\n  ]\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns a `TokenInfo` object (or array of `TokenInfo?`) containing token metadata.\n\n### circles_tables\n\nReturns all available indexed tables and namespaces that can be queried with `circles_query`.\n\n**Signature**: `circles_tables()`.\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 0,\n  \"method\": \"circles_tables\",\n  \"params\": []\n}' -H \"Content-Type: application/json\" https://rpc.circlesubi.network/\n```\n\n#### Response\n\nReturns an array of `DatabaseNamespace` objects, each containing:\n\n* `Namespace` - The namespace name (e.g., \"V_Crc\", \"CrcV1\", \"CrcV2\")\n* `Tables` - Array of `DatabaseTable` objects with table name, topic, and columns\n\n### circles_health\n\nChecks if the database is available and the indexing is progressing as expected.\n\n**Signature**: `circles_health()`.\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_health\",\n  \"params\": []\n}' -H \"Content-Type: application/json\" http://localhost:8545/\n```\n\n#### Response\n\nReturns a string status message indicating the health of the indexer.\n\n### circles_getNetworkSnapshot\n\nRetrieves a complete snapshot of the Circles network state, including all current trust relations and balances.\n\n**Signature**: `circles_getNetworkSnapshot()`.\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_getNetworkSnapshot\",\n  \"params\": []\n}' -H \"Content-Type: application/json\" https://rpc.circlesubi.network/\n```\n\n#### Response\n\nReturns a JSON object containing the complete network state with all avatars, trust relations, and balances.\n\n### circles_searchProfiles\n\nSearches for profiles by name, description, or address using full-text search.\n\n**Signature**: `circles_searchProfiles(text: string, limit?: int, offset?: int, types?: string[])`.\n\n* `text` - Search term(s)\n* `limit` - Maximum number of results (default: 20)\n* `offset` - Pagination offset (default: 0)\n* `types` - Optional array of avatar types to filter by\n\n#### Example\n\n```shell\ncurl -X POST --data '{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"circles_searchProfiles\",\n  \"params\": [\n    \"berlin\",\n    10,\n    0\n  ]\n}' -H \"Content-Type: application/json\" https://rpc.circlesubi.network/\n```\n\n#### Response\n\nReturns an array of `Profile` objects matching the search criteria.\n\n## Add a custom protocol\n\nThe plugin parses the log entries of all transaction receipts, filters them and stores them in a database.\nTo do so it needs the following information:\n\n* The event topic\n* The address of the contract that emits the event\n* A table schema for the database\n\nAll the above information are packaged into an own assembly per protocol.\nIt's structured like this:\n\n* [your-protocol].csproj\n    * `DatabaseSchema.cs` - Pulls together all information about the indexed events of a protocol.\n    * `Events.cs` - Contains the DTOs for the events (usually just Records).\n    * `LogParser.cs` - Extracts events from the transaction receipt logs.\n\n### DatabaseSchema.cs\n\nThe schema pulls together all information about the indexed events of a protocol. Each event type must have a\ncorresponding table in the database. Tables are grouped into namespaces. In practice, a namespace is just a prefix\nin front of the table name. Additionally, to the tables the schema contains a mapping of the event DTOs to the tables\nand a mapping of the event properties to the table columns.\n\n```csharp\npublic class DatabaseSchema : IDatabaseSchema\n{\n    public IDictionary\u003c(string Namespace, string Table), EventSchema\u003e Tables { get; } \n        = new Dictionary\u003c(string Namespace, string Table), EventSchema\u003e();\n    \n    public IEventDtoTableMap EventDtoTableMap { get; } = new EventDtoTableMap();\n    \n    public ISchemaPropertyMap SchemaPropertyMap { get; } = new SchemaPropertyMap();\n}\n```\n\n#### Tables\n\nThe tables are defined as a dictionary with a tuple of the namespace and table name as key and an `EventSchema` as\nvalue:\n\n```csharp\nvar transfer = new EventSchema(\n    \"CrcV1\",                                                                // Namespace\n    \"Transfer\",                                                             // Table\n    Keccak.Compute(\"Transfer(address,address,uint256)\").BytesToArray(),     // Event topic\n    [                                                                       // Columns ..\n        new (\"blockNumber\", ValueTypes.Int, true),\n        new (\"timestamp\", ValueTypes.Int, true),\n        new (\"transactionIndex\", ValueTypes.Int, true),\n        new (\"logIndex\", ValueTypes.Int, true),\n        new (\"transactionHash\", ValueTypes.String, true),\n        new (\"tokenAddress\", ValueTypes.Address, true),\n        new (\"from\", ValueTypes.Address, true),\n        new (\"to\", ValueTypes.Address, true),\n        new (\"amount\", ValueTypes.BigInt, false)\n    ]);\n\n```\n\nThe single fields/columns are defined as follows:\n\n```csharp\n public record EventFieldSchema(string Column, ValueTypes Type, bool IsIndexed, bool IncludeInPrimaryKey = false);\n```\n\nAlternatively, you can create an EventSchema from a solidity event signature:\n\n```csharp\nvar signup = EventSchema.FromSolidity(\"CrcV1\",\n        \"event Signup(address indexed user, address indexed token)\")\n```\n\n#### EventDtoTableMap\n\nEvery protocol implementation has a set of DTOs that represent the events. The `EventDtoTableMap` maps these DTOs to\nthe tables defined in the schema. The mapping is established between the generic type and the namespace and table name.\n\n```csharp\nEventDtoTableMap.Add\u003cSignup\u003e((\"CrcV1\", \"Signup\"));\n```\n\n#### SchemaPropertyMap\n\nThe `SchemaPropertyMap` maps the properties of the DTOs to the columns of the tables.\nEach column is mapped to a function that extracts the value from the DTO. The function can also return a calculated\nvalue.\n\n```csharp\nSchemaPropertyMap.Add((\"CrcV1\", \"Signup\"),\n    new Dictionary\u003cstring, Func\u003cSignup, object?\u003e\u003e\n    {\n        { \"blockNumber\", e =\u003e e.BlockNumber },\n        { \"timestamp\", e =\u003e e.Timestamp },\n        { \"transactionIndex\", e =\u003e e.TransactionIndex },\n        { \"logIndex\", e =\u003e e.LogIndex },\n        { \"transactionHash\", e =\u003e e.TransactionHash },\n        { \"user\", e =\u003e e.User },\n        { \"token\", e =\u003e e.Token }\n    });\n```\n\n### Events.cs\n\nThe events file contains the DTOs for the events. Usually, these are just records with the properties of the event.\n\n```csharp\npublic record Signup(\n    long BlockNumber,\n    long Timestamp,\n    int TransactionIndex,\n    int LogIndex,\n    string TransactionHash,\n    string User,\n    string Token) : IIndexEvent;\n```\n\nAll DTOs must derive from the `IIndexEvent` interface that specifies the basic properties necessary for pagination:\n\n```csharp\npublic interface IIndexEvent\n{\n    long BlockNumber { get; }\n    long Timestamp { get; }\n    int TransactionIndex { get; }\n    int LogIndex { get; }\n}\n```\n\n### LogParser.cs\n\nThe log parser is responsible for extracting the events from the transaction receipt logs. It must implement the\n`ILogParser` interface.\n\n```csharp\npublic class LogParser(Address emitterAddress) : ILogParser {\n    // Use the topics previously defined in the schema\n    Hash256 _transferTopic = new(DatabaseSchema.Transfer.Topic)\n    \n    public IEnumerable\u003cIIndexEvent\u003e ParseLog(Block block, TxReceipt receipt, LogEntry log, int logIndex)\n    {\n        List\u003cIIndexEvent\u003e events = new();\n        if (log.Topics.Length == 0)\n        {\n            return events;\n        }\n        \n        // Parse the log entry and add the resulting event DTOs to the list\n        var topic = log.Topics[0];\n        if (topic == _transferTopic))\n        {\n            events.Add(Erc20Transfer(block, receipt, log, logIndex));\n        }\n        \n        return events;\n    }\n}\n```\n\n### Register the protocol\n\nThe schema, property map and log parser must be registered in the main plugin file.\n\nOn first execution, the plugin will create the necessary tables in the database.\n\n___Note:___ _The plugin will not create new tables if the schema changes. You have to manually update the database\nschema._\n\n#### Register the schema\n\n```csharp\n// Add your schema to the composite schema:\nIDatabaseSchema common = new Common.DatabaseSchema();\nIDatabaseSchema v1 = new CirclesV1.DatabaseSchema();\nIDatabaseSchema v2 = new CirclesV2.DatabaseSchema();\nIDatabaseSchema customprotocol = new CustomProtocol.DatabaseSchema();\n// ...\nIDatabaseSchema databaseSchema = new CompositeDatabaseSchema([common, v1, v2, customprotocol /*, ...*/]);\n```\n\n#### Register the SchemaPropertyMap and EventDtoTableMap\n\n```csharp\n// Add your SchemaPropertyMap and EventDtoTableMap to the composite maps to initialize the sink:\nSink sink = new Sink(\n    database,\n    new CompositeSchemaPropertyMap([\n        v1.SchemaPropertyMap, v2.SchemaPropertyMap, v2NameRegistry.SchemaPropertyMap /*, ...*/\n    ]),\n    new CompositeEventDtoTableMap([\n        v1.EventDtoTableMap, v2.EventDtoTableMap, v2NameRegistry.EventDtoTableMap /*, ...*/\n    ]),\n    settings.EventBufferSize);\n```\n\n#### Register the LogParser\n\n```csharp\n// Add your log parser to the list of log parsers:\nILogParser[] logParsers =\n[\n    new CirclesV1.LogParser(settings.CirclesV1HubAddress),\n    new CirclesV2.LogParser(settings.CirclesV2HubAddress),\n    new CirclesV2.NameRegistry.LogParser(settings.CirclesNameRegistryAddress) //,\n    // ...\n];\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faboutcircles%2Fcircles-nethermind-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faboutcircles%2Fcircles-nethermind-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faboutcircles%2Fcircles-nethermind-plugin/lists"}