{"id":51332060,"url":"https://github.com/magicblock-labs/magic-router","last_synced_at":"2026-07-01T23:32:36.768Z","repository":{"id":367416719,"uuid":"894897410","full_name":"magicblock-labs/magic-router","owner":"magicblock-labs","description":"JSON-RPC router for ephemeral rollups","archived":false,"fork":false,"pushed_at":"2026-06-25T22:04:26.000Z","size":420,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-06-26T00:05:53.425Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","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/magicblock-labs.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-11-27T07:43:36.000Z","updated_at":"2026-06-01T12:09:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/magicblock-labs/magic-router","commit_stats":null,"previous_names":["magicblock-labs/magic-router"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/magicblock-labs/magic-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magicblock-labs%2Fmagic-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magicblock-labs%2Fmagic-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magicblock-labs%2Fmagic-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magicblock-labs%2Fmagic-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/magicblock-labs","download_url":"https://codeload.github.com/magicblock-labs/magic-router/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/magicblock-labs%2Fmagic-router/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35027341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"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-07-01T23:32:35.409Z","updated_at":"2026-07-01T23:32:36.760Z","avatar_url":"https://github.com/magicblock-labs.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MagicBlock RPC Router\n\nThe MagicBlock RPC Router is an auxiliary service designed to provide dynamic\nrequest routing capabilities within the MagicBlock Ephemeral Rollups (ER)\ninfrastructure. Acting as a single endpoint, it efficiently manages a subset of\nJSON-RPC requests as outlined in the [Solana\nspecification](https://solana.com/docs/rpc).\n\n## Overview\n\nThe dynamic routing mechanism works by ascertaining the delegation status of\nany given Solana account. Each account can be in one of two delegation states:\n\n1. **Undelegated**: The account's most current state resides on the main chain\n   (mainnet, testnet, devnet).\n2. **Delegated**: This state is accompanied by the identity of the ER to which\n   the account is delegated.\n\nUpon encountering a new account, the router retrieves its delegation state and\nestablishes a WebSocket subscription to monitor this status. Consequently, the\ninitial request for any new account incurs additional latency due to these\npreliminary steps.\n\nBy keeping track of each account's delegation status, the router dynamically\nroutes requests: directing undelegated accounts to the main chain and delegated\naccounts to their respective ER.\n\n### ER Identity\n\nAn ER identity comprises the public key of the ER validator. To facilitate\nrouting, the router determines the fully qualified domain name (FQDN) of a\nvalidator given its public key. This is achieved through an on-chain database\nof ER nodes, which the router accesses to map identities to FQDNs. Updates to\nthis database are propagated to the router via a WebSocket subscription.\n\n## Supported Requests\n\nGiven the ephemeral nature of rollups, not all requests from the Solana RPC\nspecification are supported. The router supports a select subset of these\nrequests:\n\n### HTTP\n\n1. [getAccountInfo](https://solana.com/docs/rpc/http/getaccountinfo)\n2. [getMultipleAccounts](https://solana.com/docs/rpc/http/getmultipleaccounts)\n3. [getBalance](https://solana.com/docs/rpc/http/getbalance)\n4. [getTokenAccountBalance](https://solana.com/docs/rpc/http/gettokenaccountbalance)\n5. [getIdentity](https://solana.com/docs/rpc/http/getidentity), this method is used to return an identity of the ER validator which is closest to the\n   router, latency wise.\n6. [sendTransaction](https://solana.com/docs/rpc/http/sendtransaction) - the\n   sent transaction is parsed to extract all the explicitly referenced\n   writeable accounts (lookup tables are not supported). If any of those\n   accounts is delegated then all of the delegated writeable accounts in the\n   transaction should be delegated to the same ER, otherwise the method will\n   return an error. The matched route for the transaction signature will linger\n   in the router for a while, so, methods like `getSignatureStatuses` or\n   `getTransaction` can be routed to the same upstream\n7. [getTransaction](https://solana.com/docs/rpc/http/gettransaction) - only\n   makes sense for transactions which were recently sent through the router\n8. [getSignatureStatuses](https://solana.com/docs/rpc/http/getsignaturestatuses) - only makes sense for transactions which were recently sent through the router\n9. [getSignaturesForAddress](https://solana.com/docs/rpc/http/getsignaturesforaddress)\n10. [getFirstAvailableBlock](https://solana.com/docs/rpc/http/getfirstavailableblock) - dummy\n    method used primarily for compatibility with solana explorer, the returned\n    value should not be used for any decision making\n11. [isBlockhashValid](https://solana.com/docs/rpc/http/isblockhashvalid)\n12. [getLatestBlockHash](https://solana.com/docs/rpc/http/getlatestblockhash) -\n    a best effort method which returns the blockhash from the closest ER node.\n    Note, if the network topology changes, the closest ER along with it, then\n    it will cause transactions signed with the given blockhash to fail, as it\n    was obtained from the wrong ER\n13. [getEpochSchedule](https://solana.com/docs/rpc/http/getEpochSchedule) -\n    dummy\n    method used primarily for compatibility with solana explorer, the returned\n    value should not be used for any decision making\n14. [getEpochInfo](https://solana.com/docs/rpc/http/getEpochInfo) - dummy\n    method used primarily for compatibility with solana explorer, the returned\n    value should not be used for any decision making\n\n### Websocket\n\n1. [signatureSubscribe](https://solana.com/docs/rpc/websocket/signaturesubscribe) - any transaction processed by the router is temporarily retained together with the upstream metadata that was used to forward it. This same upstream metadata is then reused when forwarding subscription updates.\n\n### Custom Methods\n\nThere're also a few methods which are unique to the router, and as a result\nthey go beyond the Solana JSON RPC API spec.\n\n1. **getRoutes** - a custom method to query all the ER nodes known to the\n   router\n2. **getBlockhashForAccounts** - another custom method to query the blockhash\n   for the provided list of accounts. The list of accounts is usually delegated\n   writeable accounts from the transaction that needs to be signed. If the\n   accounts are delegated to different ER nodes, the method will return an\n   error. If the accounts are not delegated the returned blockhash is from the\n   base chain.\n3. **getDelegationStatus** - custom method which retrieves the delegation\n   status of the account along with the parsed delegation record (if\n   applicable)\n\n## API Documentation\n\nFind details on API documentation on [official MagicBlock documentation] (https://docs.magicblock.gg/api-reference/).\n\n### getRoutes\n\nReturns information about all Ephemeral Rollup (ER) nodes known to the router.\n\n**Example Request:**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"getRoutes\"\n}\n```\n\n**Example Response:**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"result\": [\n    {\n      \"identity\": \"7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU\",\n      \"fqdn\": \"er-node-1.magicblock.com\",\n      \"baseFee\": 5000,\n      \"blockTimeMs\": 400,\n      \"countryCode\": \"US\"\n    },\n    {\n      \"identity\": \"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM\",\n      \"fqdn\": \"er-node-2.magicblock.com\",\n      \"baseFee\": 3000,\n      \"blockTimeMs\": 350,\n      \"countryCode\": \"EU\"\n    }\n  ]\n}\n```\n\n**Response Fields:**\n\n- `identity` (string): The public key of the ER validator\n- `fqdn` (string): Fully qualified domain name of the ER node\n- `baseFee` (number): Base fee in lamports for transactions on this ER\n- `blockTimeMs` (number): Block time in milliseconds for this ER\n- `countryCode` (string): ISO country code where the ER node is located\n\n### getBlockhashForAccounts\n\nReturns the latest blockhash for a list of accounts. This method is necessary in order to abstract away the splitting in the frontend.\n\n**Example Request:**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"getBlockhashForAccounts\",\n  \"params\": [\n    [\n      \"7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU\",\n      \"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM\"\n    ]\n  ]\n}\n```\n\n**Example Response:**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"result\": {\n    \"blockhash\": \"5KKsxSGdJYwBv3rcGZhdJzE3bqJj2vJxP2JxP2JxP2JxP\",\n    \"lastValidBlockHeight\": 123456789\n  }\n}\n```\n\n**Response Fields:**\n\n- `blockhash` (string): The latest blockhash as a base58-encoded string\n- `lastValidBlockHeight` (number): The block height at which this blockhash expires\n\n**Behavior:**\n\n- If all accounts are **undelegated**: Returns the blockhash from the base chain\n- If all accounts are **delegated to the same ER**: Returns the blockhash from that ER\n- If accounts are **delegated to different ERs**: Returns an error (conflicting delegations)\n- If **no accounts are provided**: Returns the blockhash from the base chain\n\n**Error Cases:**\n\n- `ConflictingDelegations`: When accounts are delegated to different ER nodes\n- `UnknownErNode`: When an ER node is not found in the routing table\n\n### getDelegationStatus\n\nReturns the delegation status along with ER record for the given account\n\n**Example Request:**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"getDelegationStatus\",\n  \"params\": [\"7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU\"]\n}\n```\n\n**Example Response 1:**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"result\": {\n    \"isDelegated\": false\n  }\n}\n```\n\n**Example Response 2:**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"result\": {\n    \"isDelegated\": true,\n    \"delegationRecord\": {\n      \"authority\": \"11111111111111111111111111111111\",\n      \"owner\": \"3JnJ727jWEmPVU8qfXwtH63sCNDX7nMgsLbg8qy8aaPX\",\n      \"delegationSlot\": 388473478,\n      \"lamports\": 15144960\n    }\n  }\n}\n```\n\n**Response Fields:**\n\n- `isDelegated` (bool): flag indicating whether the account is delegated\n- `delegationRecord` (object): the parsed delegation record for the delegated account (not present for non-delegated accounts)\n  - `authority` (string): the ER identity (authority), which was used to delegated the account to\n  - `owner` (string): the original owner of the delegated account\n  - `delegationSlot` (number): base chain slot, at which the account has been delegated\n  - `lamports` (number): base chain balance of the account, recorded at the moment of delegation\n\n**Error Cases:**\n\n- `UnknownErNode`: When an ER node is not found in the routing table\n\n## Supported WebSocket Subscriptions\n\nIn addition to HTTP requests, the router can dynamically manage WebSocket\nsubscriptions. Currently, only\n[accountSubscribe](https://solana.com/docs/rpc/websocket/accountsubscribe) is\nsupported.\n\nSubscriptions are managed by establishing one connection to the main chain and\nan additional optional connection to the ER node if the account is delegated.\nUpdates from these upstream sources are seamlessly relayed to the client.\nShould the delegation status change, the router transparently adjusts\nsubscription sources, ensuring a continuous stream of updates.\n\n### Latency Considerations\n\nThe router's additional processing and bookkeeping introduce increased latency\nfor routed requests. To mitigate this, it is recommended to deploy the router\nin proximity to the client, ideally on the same host or within the same data\ncenter.\n\n## Configuration\n\nThe router service is configured using a TOML file with the following options:\n\n```toml\n# Router Configuration\n\n# Listening IP address and port for incoming connections.\nlisten-address = \"127.0.0.1:8080\"\n\n# List of base URLs for chain nodes; employs round-robin load balancing.\nbase-chain-urls = [\"https://api.devnet.solana.com\"]\n\n# Maximum delegation cache entries.\nmax-cached-delegations = 1000\n# Maximum number of transaction to route mappings to keep in memory\nmax-cached-transactions = 16384\n\n# Maximum simultaneous connections the router can handle.\nmax-connections = 1024\n\n# Maximum subscriptions allowed per connection.\nmax-subscriptions-per-connection = 1024\n\n# Frequency of ping requests, performed to upstream nodes, to determine their proximity (latency wise)\nproximity-ping-frequency-sec = 30\n\n# WebSocket Configuration\n[websocket]\n# Ping interval for maintaining WebSocket connections, in seconds.\nping-interval-sec = 30\n\n# Number of connections per upstream WebSocket server.\nconnections-per-upstream = 5\n```\n\n## Deployment\n\nTo deploy the router, compile the project in release mode:\n\n```sh\ncargo build --release\n```\n\nThen, execute the router with the specified configuration file:\n\n```sh\nmagicblock-rpc-router config.toml\n```\n\nThe service will initiate and begin accepting connections.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmagicblock-labs%2Fmagic-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmagicblock-labs%2Fmagic-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmagicblock-labs%2Fmagic-router/lists"}